Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1110736
  • 博文数量: 165
  • 博客积分: 3900
  • 博客等级: 中校
  • 技术积分: 1887
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-06 15:15
文章分类

全部博文(165)

文章存档

2020年(3)

2019年(8)

2017年(2)

2016年(8)

2015年(14)

2013年(15)

2012年(32)

2011年(11)

2010年(14)

2009年(7)

2008年(20)

2007年(31)

分类: Android平台

2013-07-12 11:05:34

Android RIL是基于telephony 服务和raido 硬件层的抽象层, 通过研究RIL的代码可以看到,Android的rild库是介于HAL接口与basebandmodem之间,它同样提供了语音、数据、短信、SIM卡管理以及STK应用的功能,实现思路跟微软的RIL有异曲同工之妙,也是把标准的 GSM27.007中常用的如dial这些做主动请求的操作称之为request,一共75个;另外一类GSM模块主动上报的例如信号强度、基站信息等,称之为unsolicited response,一共17个;开发模式也是跟微软RIL开发差不多,需要针对不同的GSM模块进行不同的GSM驱动开发,公用的部分google给你做好了,特定的部分需要你自己去定制,这样做可以大大地提高开发效率。以下是RIL 交互图

2.本地代码 :

 

RIL 支持的本地代码包括 ril 库和守护进程:

hardware/ril/include

hardware/ril/libril

hardware/ril/rild

hardware/ril/reference-ril编译结果是

/system/bin/rild :守护进程

/system/lib/libril.so : RIL 的库

/system/lib/libreference-ril.so : RIL 参考库3.RILInitialization

Android initializes the telephony stack andthe Vendor RIL at startup as described in the sequence below:

(1). RIL daemon reads rild.lib path andrild.libargs system properties to determine the Vendor RIL library to use andany initialization arguments to provide to the Vendor RIL

(2). RIL daemon loads the Vendor RILlibrary and calls RIL_Init to initialize the RIL and obtain a reference to RILfunctions

(3). RIL daemon calls RIL_register on theAndroid telephony stack, providing a reference to the Vendor RIL functions

See the RIL Daemon source code at//device/commands/rild/rild.c for details.

4.rild 执行流程

rild 是一个守护进程,在这里宏RIL_SHLIB 被定义。执行的过程为:

获取参数 -> 打开功能库 -> 建立事件循环(线程) -> 执行 RIL_Init ->RIL_register 。


  1.    
  2. int main(int argc,  
  3. char **argv)  
  4.    
  5.    {  
  6.    
  7.    /*获取参数并解析 */  
  8.    
  9.       dlHandle = dlopen(rilLibPath, RTLD_NOW);  
  10.    
  11.    /*启动线程,进入事件循环 */  
  12.    
  13.       RIL_startEventLoop();  
  14.    
  15.       rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int,char  
  16.    
  17. **))  
  18.    
  19.                   dlsym(dlHandle,"RIL_Init");  
  20.    
  21.    /*处理参数 */  
  22.    
  23.       funcs = rilInit(&s_rilEnv, argc, rilArgv);  
  24.    
  25.       RIL_register(funcs);  
  26.    
  27.   done:  
  28.    
  29.       while(1) {  
  30.    
  31.           sleep(0x00ffffff);  
  32.    
  33.       }  
  34.    
  35.    }  





5.RIL Interaction

RIL有两种执行流程储:

(1)Solicited commands: 基于RIL lib, 例如 DIAL andHANGUP.

(2)Unsolicited responses: 基于 baseband, 例如CALL_STATE_CHANGED和 NEW_SMS.

5.1 Solicited

以下两段代码是请求类接口:

void OnRequest (int request_id, void *data,size_t datalen, RIL_Token t);

void OnRequestComplete (RIL_Token t,RIL_Error e, void *response, size_t responselen);The following diagramillustrates a solicited call in Android.

5.2 Unsolicited

以下代码是非请求类接口:

void OnUnsolicitedResponse (intunsolResponse, void *data, size_t datalen);The following diagram illustrates anunsolicited call in Android.

6.RIL_Init

使用自定义的RIL lib 时,由于rild通过符号RIL_Init获取一组函数指针并以此与之建立联系,因而必须实现RIL_Init 函数,,RIL_Init 的定义如下:

RIL_RadioFunctions *RIL_Init (RIL_Env* env,int argc, char **argv);RIL_Init should return a RIL_RadioFunctions structurecontaining the handles to the radio functions:


  1. type structure {  
  2.        int RIL_version;  
  3.        RIL_RequestFunc onRequest;  
  4.        RIL_RadioStateRequest onStateRequest;       
  5.        RIL_Supports supports;  
  6.        RIL_Cancel onCancel;  
  7.        RIL_GetVersion getVersion;  
  8. }  




RIL_RadioFunctions;7.接下来分析初始化流程

主入口是rild.c中的main函数,主要完成三个任务:

(1). 开启libril.so中的event机制, 在RIL_startEventLoop中,是最核心的由多路I/O驱动的消息循环。

(2). 初始化librefrence_ril.so,也就是跟硬件或模拟硬件modem通信的部分(后面统一称硬件), 通过RIL_Init函数完成。

(3). 通过RIL_Init获取一组函数指针RIL_RadioFunctions, 并通过RIL_register完成注册,并打开接受上层命令的socket通道。

8.RIL跟上层通讯主要采用两种方式:

(1)一种是通过Socket发送与接收消息的方式来实现,

C方面,这个Socket在ril.cpp里面可以找到它的创建代码:


  1. s_fdListen =android_get_control_socket(SOCKET_NAME_RIL);JAVA方面,在RIL.java中:  
  2.          s = new LocalSocket();  
  3.          l = new LocalSocketAddress(SOCKET_NAME_RIL,  
  4.                            LocalSocketAddress.Namespace.RESERVED);  



         s.connect(l);(2)还有另外一种方式就是直接通过TCP/IP直接访问内核中的shared memory,进行RPC调用,这种方式主要应用在数据模式上,一来由于Android的每个Activity随时都会有可能需要网络连接接收发送数据,因此必须提供一种实时性较高访问的方式,二来可以提高通讯效率。

参考资料:

http://www.netmite.com/android/mydroid/development/pdk/docs/telephony.html


========================================================


Android 2.2 RIL hardware 部分代码简介


Android源码中,hardware/ril目录中包含着RILhardware 底层源码,该目录树如下引用部分,下面将做具体的分析:

|– CleanSpec.mk

|– include

| `– telephony

| |– ril.h

| `– ril_cdma_sms.h

|– libril

| |– Android.mk

| |– MODULE_LICENSE_APACHE2

| |– NOTICE

| |– ril.cpp

| |– ril_commands.h

| |– ril_event.cpp

| |– ril_event.h

| `– ril_unsol_commands.h

|– reference-cdma-sms

| |– Android.mk

| |– reference-cdma-sms.c

| `– reference-cdma-sms.h

|– reference-ril

| |– Android.mk

| |– MODULE_LICENSE_APACHE2

| |– NOTICE

| |– at_tok.c

| |– at_tok.h

| |– atchannel.c

| |– atchannel.h

| |– misc.c

| |– misc.h

| `– reference-ril.c

`– rild

|– Android.mk

|– MODULE_LICENSE_APACHE2

|– NOTICE

|– radiooptions.c

|– rild.c

`– rild.c~

一、目录hardware/ril/include

 

包含两个头文件: ril.h 和 ril_cdma_sms.h ,其中 ril.h中定义了76个如下类型的宏:RIL_REQUEST_XXX ,这些宏代表着客户进程可以向Android telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等。 ril_cdma_sms.h 则是针对cdma sms 的扩展时所用到的一些宏和结构体的定义。

二、目录hardware/ril/libril

该目录下代码负责与上层客户进程进行交互。在接收到客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。

1、文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。诸如:

   {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},

   {RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},

   {RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},

   {RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},

   {RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},

   {RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts},

   ... .. ..2、文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;诸如:

   {RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, responseVoid, WAKE_PARTIAL},

   {RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, responseVoid, WAKE_PARTIAL},

   {RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED, responseVoid, WAKE_PARTIAL},

   {RIL_UNSOL_RESPONSE_NEW_SMS, responseString, WAKE_PARTIAL},

   {RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, responseString,WAKE_PARTIAL},

   {RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM, responseInts, WAKE_PARTIAL},

    .. . . . .3、文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending -> ev->func())。

4、文件ril.cpp 功能较为庞大,如下:

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

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

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

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

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

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

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

三、目录hardware/ril/reference-ril

本目录下代码主要负责与modem(调制解调器)进行交互。

1、文件reference-ril.c:

此文件核心是两个函数:onRequest和onUnsolicited

1) onRequest 函数:在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的ATcommand,发送给modem,然后睡眠等待。当收到此AT command的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete -> sendResponse)。

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

2、文件atchannel.c:

负责向modem读写数据。其中,写数据(主要是AT command)功能运行在主线程中,读数据功能运行在一个单独的读线程中。

函数at_send_command_full_nolock:运行在主线程里面。将一个AT command命令写入modem后进入睡眠状态(使用 pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了AT command的最终响应并返回。函数readerLoop运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前AT command的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(AT command的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。

3、文件at_tok.c 提供AT响应的解析函数

4、misc.c 字面意思杂项,里面就提供一个字符串匹配函数

四、目录hardware/ril/rild

该目录下的代码主要是为了生成rild 和 radiooptions 的可执行文件

1、radiooptions.c 生成radiooptions 的可执行文件, radooptions程序仅仅是把命令行参数传递给socket{rild-debug}去处理而已,从而达到与rild通信,可供调试时配置Modem参数。

2、rild.c 生成 rild 的可执行文件有关rild 进程的介绍看 AndroidRIL架构学习总结


=================================================


Android 2.2 RIL 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/TelephonyRegistry.java

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

TelephonyManager

TelephonyManager主要使用两个服务来访问telephony功能:

1. ITelephony, 提供与telephony 进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。

2. ITelephonyRegistry, 提供登记telephony事件的接口。由frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。

interface CommandsInterface

interface CommandsInterface 描述了对电话的所有操作接口,如命令,查询状态,以及电话事件监听等

class BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。

而class RIL又派生自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

interface Phone描述了对电话的所有操作接口。 PhoneBase直接从Phone 派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA 和GSM的操作。

PhoneProxy也从Phone直接派生而来。当当前不需要区分具体是CDMAPhone还是GSM Phone时,可使用PhoneProxy。

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

interface PhoneNotifier

interface PhoneNotifier定义电话事件的通知方法

DefaultPhoneNotifier从PhoneNotifier派生而来。在其方法实现中,通过调用service ITelephonyRegistry来发布电话事件。

service ITelephonyRegistey由frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。这个类通过广播intent,从而触发对应的broadcast receiver。

在PhoneApp创建时,sPhoneNotifier = new DefaultPhoneNotifier();…sCommandsInterface = new RIL(context, networkMode,cdmaSubscription);

然后根据当前phone是cdma还是gsm,创建对应的phone,如:sProxyPhone = newPhoneProxy(new GSMPhone(context,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 = newGsmCallTracker(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

RILRequest rr =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);

hardware 相关可以参考:Android2.2 RIL hardware 部分代码简介 

======================================================



Android phone app 默认启动理解以及对Java端入口的分析



Android phone app 默认的进程是com.android.phone ,该进程有个特性,当你kill 它时,它会重新在创建。很多人可能没弄清为什么,它的运行模式和其他app不同呢?以下我将个人的简单认识稍未阐述下:

1、首先PhoneApp.apk 存放于System/app 目录下,这决定了它拥有超于其他data/app 权限的能力。显然是因为它有系统级的属性。

2、其次在AndroidManifest.xml 中有以下描述:

   

                android:persistent="true"

                android:label="@string/dialerIconLabel"

                 android:icon="@drawable/ic_launcher_phone">其中 android:persistent=”true”决定了它必须一只运行,官方对该属性的说明如下:

android:persistent

   Whether or not the application should remain running at all times —"true" if it should, and "false" if not. The default valueis "false". Applications should not normally set this flag;persistence mode is intended only for certain system applications.

以上两部分内容决定了com.android.phone,该进程将一值存在于系统中。

默认启动的App,没有调用Activity,那它默认的Java入口是那里?调用了什么类包?

Android 的应用进程都是fork出来的,而且fork出的进程是直接进入自己的事件循环,拿个一般的App的启动流程来分析:

1、首先点击一个并未启动的应用Venus,这时它会先fork出一个进程,然后进入它自己的事件循环。

2、进入事件循环的之后做的第一件事并不是为了做响应点击事件的处理,而是检测该App是否有Application 的默认入口,如上例子中的:android:name=”PhoneApp”就是Application 的入口类包名。有的话检测该类是否继承于Application, 没有入口的话,进入第三步

3、接着响应点击事件,ActivityManager 就找寻该应用对应的MainActivity入口,并创建该类.

总结PhoneApp 的Java端入口

以PhoneApp.java 类内容来看

[java] view plaincopy
  1. package com.android.phone;  
  2.    
  3. import *****;  
  4.    
  5. /** 
  6.  * Top-level Application class for the Phone app. 
  7.  */  
  8. public class PhoneApp extends Application implements AccelerometerListener.OrientationListener {  
  9.     /* package */ static final String LOG_TAG = "PhoneApp";  
  10.    
  11.     private static PhoneApp sMe;  
  12.    
  13.     public PhoneApp() {  
  14.         sMe = this;  
  15.     }  
  16.    
  17.     static PhoneApp getInstance() {  
  18.         return sMe;  
  19.     }  
  20.    
  21. }  

以上面代码来看,继承于Application 的PhoneApp 当作了phone进程的入口。

   public PhoneApp() {

       sMe = this;

    }该方法说明继承于Application的类,必须可以被外部实例, 实际上在启动过程中,如果有该入口,就是必须先实例该类的。Android 提供该部分接口,是为了有些应用需要常驻内存。这些应用可能需要提供一些provider或service之类,但这些功能又必须先于Activity而存在,因而提供该入口让java 的应用有个类似于main函数的入口,做些初始化的工作。
阅读(5320) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~