Android RIL 原生代码(c/c++) 和 java代码部分分析

Android系统源代码目录里面: hardware/ril 目录包含了所有有关于telephony的底层代码.

1.目录架构(20101215的git版本):
  1. ril
  2. |-- CleanSpec.mk
  3. |-- include
  4. |   `-- telephony
  5. |       |-- ril_cdma_sms.h
  6. |       `-- ril.h
  7. |-- libril
  8. |   |-- Android.mk
  9. |   |-- MODULE_LICENSE_APACHE2
  10. |   |-- NOTICE
  11. |   |-- ril_commands.h
  12. |   |-- ril.cpp
  13. |   |-- ril_event.cpp
  14. |   |-- ril_event.h
  15. |   `-- ril_unsol_commands.h
  16. |-- reference-cdma-sms
  17. |   |-- Android.mk
  18. |   |-- reference-cdma-sms.c
  19. |   `-- reference-cdma-sms.h
  20. |-- reference-ril
  21. |   |-- Android.mk
  22. |   |-- atchannel.c
  23. |   |-- atchannel.h
  24. |   |-- at_tok.c
  25. |   |-- at_tok.h
  26. |   |-- misc.c
  27. |   |-- misc.h
  28. |   |-- MODULE_LICENSE_APACHE2
  29. |   |-- NOTICE
  30. |   `-- reference-ril.c
  31. `-- rild
  32.     |-- Android.mk
  33.     |-- MODULE_LICENSE_APACHE2
  34.     |-- NOTICE
  35.     |-- radiooptions.c
  36.     `-- rild.c

  37. 6 directories, 29 files
复制代码


其中include/telephony目录下面的ril.h文件,定义了104个如下的宏:
RIL_REQUEST_XXXX ,
这些宏代表客户进程向Android telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等等。

2.目录hardware/ril/libril
本目录下代码负责与客户进程进行交互。在接收客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。
  • 文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。
  • 文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;以及WAKEType???
  • 文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending-> ev->func())
  • 文件ril.cpp

         RIL_register函数 :打开监听端口,接收来自客户进程的命令请求 (s_fdListen =android_get_control_socket(SOCKET_NAME_RIL);),当与某客户进程连接建立时,调用listenCallback函数;创建一单独线程监视并处理所有事件源(通过ril_event_loop)

         listenCallback函数 :当与客户进程连接建立时,此函数被调用。此函数接着调用processCommandsCallback处理来自客户进程的命令请求

           processCommandsCallback函数 :具体处理来自客户进程的命令请求。对每一个命令,ril_commands.h中都规定了对应的命令处理函数(dispatchXXX),processCommandsCallback会调用这个命令处理函数进行处理。

          dispatch系列函数 :此函数接收来自客户进程的命令己相应参数,并调用onRequest进行处理。

          RIL_onUnsolicitedResponse函数 :将来自网络端的事件封装(通过调用responseXXX)后传给客户进程。

         RIL_onRequestComplete函数 :将命令的最终响应结构封装(通过调用responseXXX)后传给客户进程。

          response系列函数 :对每一个命令,都规定了一个对应的response函数来处理命令的最终响应;对每一个网络端的事件,也规定了一个对应的response函数来处理此事件。response函数可被onUnsolicitedResponse或者onRequestComplete调用。

3. 目录hardware/ril/reference-ril分析:

   本目录下代码主要负责与modem进行交互。
  • 文件reference-ril.c:此文件核心是两个函数:onRequest和onUnsolicited
        onRequest 函数 :在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的ATcommand,发送给modem,然后睡眠等待。当收到此ATcommand的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete-> sendResponse)。

        onUnsolicited函数 :这个函数处理modem从网络端收到的各种事件,如网络信号变化,拨入的电话,收到短信等。然后将时间传给客户进程(RIL_onUnsolicitedResponse -> sendResponse)

  • 文件atchannel.c:负责向modem读写数据。其中,写数据(主要是ATcommand)功能运行在主线程中,读数据功能运行在一个单独的读线程中。
        at_send_command_full_nolock函数 :运行在主线程里面。将一个ATcommand命令写入modem后进入睡眠状态(使用pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了ATcommand的最终响应并返回。

         readerLoop函数:  运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前ATcommand的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(ATcommand的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。



第二部分, Java代码
Android中,telephony相关的java代码主要在下列目录中:
  1. frameworks/base/telephony/java/android/telephony
  2. frameworks/base/telephony/java/com/android/internal/telephony
  3. frameworks/base/services/java/com/android/server
  4. packages/apps/Phone
复制代码


其中,目录 telephony/java/android/telephony 中的代码提供Androidtelephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager。
目录 telephony/java/com/android/internal/telephony services/java/com/android/server 中的代码提供一系列内部接口,目前第三方应用还不能使用。当前似乎只有 packages/apps/Phone 能够使用
目录 packages/apps/Phone 是一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。

TelephonyManager (telephony/java/android/telephony/TelephonyManager.java)主要使用两个服务来访问telephony功能:
    1.  ITelephony , 提供与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
   2.  ITelephonyRegistry ,提供登记telephony事件的接口。由frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。

interface CommandsInterface  描述了对电话的所有操作接口,如命令, 查询状态,以及电话事件监听等。
classBaseCommands 是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。
classRIL 又派生自BaseCommands。RIL负责实际实现CommandsInterface中的接口方法。RIL通过Socket和rild守护进程进行通讯。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,发送给rild。线程RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种,
1. 电话事件,RIL_UNSOL_xxx, RIL读取相应数据后,发送message给对应的handler(详见函数processUnsolicited)
2. 命令的异步响应。(详见函数processSolicited)

interface Phone 描述了对电话的所有操作接口。 PhoneBase直接从Phone派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA和GSM的操作。
PhoneProxy 也从Phone直接派生而来。当当前不需要区分具体是CDMA Phone还是GSMPhone时,可使用PhoneProxy。

抽象类 Call 代表一个call,有两个派生类CdmaCall和GsmCall。

interface PhoneNotifier 定义电话事件的通知方法
DefaultPhoneNotifier 从PhoneNotifier派生而来。在其方法实现中,通过调用serviceITelephonyRegistry来发布电话事件。
serviceITelephonyRegistey 由frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。这个类通过广播intent,从而触发对应的broadcastreceiver。

在PhoneApp创建时,
  1.   sPhoneNotifier = newDefaultPhoneNotifier();
  2.    ...
  3.    sCommandsInterface = newRIL(context, networkMode, cdmaSubscription);
复制代码

然后根据当前phone是cdma还是gsm,创建对应的phone,如
  1.    sProxyPhone = newPhoneProxy(new GSMPhone(context,
  2.                                                       sCommandsInterface, sPhoneNotifier));
复制代码




下面我们来研究一个电话打出去的流程。
1. TwelveKeyDialer.java, onKeyUp()
2. TwelveKeyDialer.java, placeCall()
3. OutgoingCallBroadcaster.java, onCreate()
   sendOrderedBroadcast(broadcastIntent, PERMISSION,
               new OutgoingCallReceiver(), null, Activity.RESULT_OK, number,null);
4. OutgoingCallBroadcaster.java, OutgoingCallReceiver
    doReceive-> context.startActivity(newIntent);
5. InCallScreen.java, onCreate/onNewIntent
6. InCallScreen.java, placeCall
7. PhoneUtils.java, placeCall
8. GSMPhone.java, dial
9. GsmCallTracker.java, dial
10. RIL.java, dial
     RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
     ...
     send(rr);

下面来研究一个incoming call的流程:
1. 创建GsmPhone时,mCT = new GsmCallTracker(this);
2. 创建GsmCallTracker时:
   cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE,null);-->    
   mCallStateRegistrants.add(r); 
3. RIL中的RILReceiver线程首先读取从rild中传来的数据:processResponse-> processUnsolicited
4. 对应于incoming call,RIL收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,触发mCallStateRegistrants中的所有记录。
5.GsmCallTracker处理EVENT_CALL_STATE_CHANGE,调用pollCallsWhenSafe
6. 函数pllCallsWhenSafe 处理:
    lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
    cm.getCurrentCalls(lastRelevantPoll);
7. RIL::getCurrentCalls
    RILRequestrr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS,result);
    ...
   send(rr);
8.接着RIL调用processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS的返回结果
9.GsmCallTracker的handleMessage被触发,处理事件EVENT_POLL_CALLS_RESULT,调用函数  
   handlePollCalls
10. handlPollCalls 调用
   phone.notifyNewRingingConnection(newRinging);

11. PhoneApp中创建CallNotifier
12. CallNotifier注册:

   registerForNewRingingConnection ->mNewRingingConnectionRegistrants.addUnique(h, what, obj);

当我们开始编写Android的电话应用程序的时候,如果需要进行电话拨号,可以进行如下调用:
ITelephony tpCallModule = (ITelephony)ITelephony.Stub.asInterface(ServiceManager.getService("phon"));
tpCallModule.dial("13800138000");
而对于短信的应用,我们需要调用的则是SmsManager:
SmsManager SMS = SmsManager.getDefault();
SMS.sendTextMessage("13420926323",null,"this is a test sms",null,null);
到底Android是如何跟底层GSM模块通讯的呢?我一开始就猜测是不是跟WM那样采用RIL的架构来实现,查看了google的官方文档,证实了我的猜测是正确的,Android里面的确采用了RIL架构跟底层GSM模块通讯,先看看RIL在Android里的位置吧。

Android RIL 
Android的RIL在这里被做成一个叫做rild的库,通过一个系统环境变量ro.radio.noril是否为空来决定要不要把RIL的代码编译进内核,这点跟CE的编译方式是一样的,如果设置了该变量,将会有个"SimulateCommand"的玩意编译进内核,它可以在没有实际GSM硬件的情况下模拟部分实际硬件的指令,然后让RIL驱动提供给上层应用,如果没有设置该系统环境变量就会采用RIL。
通过研究RIL的代码可以看到,Android的rild库是介于HAL接口与baseband modem之间,它同样提供了语音、数据、短信、SIM卡管理以及STK应用的功能,实现思路跟微软的RIL有异曲同工之妙,也是把标准的GSM27.007中常用的如dial这些做主动请求的操作称之为request,一共75个;另外一类GSM模块主动上报的例如信号强度、基站信息等,称之为unsolicited response,一共17个;开发模式也是跟微软RIL开发差不多,需要针对不同的GSM模块进行不同的GSM驱动开发,公用的部分google给你做好了,特定的部分需要你自己去定制,这样做可以大大地提高开发效率。
RIL跟上层通讯主要采用两种方式,一种是通过Socket发送与接收消息的方式来实现,这个Socket在ril.cpp里面可以找到它的创建代码:
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
还有另外一种方式就是直接通过TCP/IP直接访问内核中的shared memory,进行RPC调用,这种方式主要应用在数据模式上,一来由于Android的每个Activity随时都会有可能需要网络连接接收发送数据,因此必须提供一种实时性较高访问的方式,二来可以提高通讯效率。
其实最上层的ITelephony和SmsManager有点像WM里面的Connection Manager,呵呵,有意思,这是不是其实印证了一句话“万变不离其宗”呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值