Android RIL 架構學習總結

 

1.Android RIL 概念     (轉自http://newfaction.net/2011/03/08/android-ril-structure-learning-summary.html)

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 。

 
int main(int argc,
char **argv)
 
   {
 
   /*獲取參數並解析 */
 
      dlHandle = dlopen(rilLibPath, RTLD_NOW);
 
   /*啓動線程,進入事件循環 */
 
      RIL_startEventLoop();
 
      rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int,char
 
**))
 
                  dlsym(dlHandle,"RIL_Init");
 
   /*處理參數 */
 
      funcs = rilInit(&s_rilEnv, argc, rilArgv);
 
      RIL_register(funcs);
 
  done:
 
      while(1) {
 
          sleep(0x00ffffff);
 
      }
 
   }



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:

type structure {
       int RIL_version;
       RIL_RequestFunc onRequest;
       RIL_RadioStateRequest onStateRequest;     
       RIL_Supports supports;
       RIL_Cancel onCancel;
       RIL_GetVersion getVersion;
}


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裏面可以找到它的創建代碼:

s_fdListen =android_get_control_socket(SOCKET_NAME_RIL);JAVA方面,在RIL.java中:
         s = new LocalSocket();
         l = new LocalSocketAddress(SOCKET_NAME_RIL,
                           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 中有以下描述:

   <application android:name="PhoneApp"

                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 類內容來看

package com.android.phone;
 
import *****;
 
/**
 * Top-level Application class for the Phone app.
 */
public class PhoneApp extends Application implements AccelerometerListener.OrientationListener {
    /* package */ static final String LOG_TAG = "PhoneApp";
 
    private static PhoneApp sMe;
 
    public PhoneApp() {
        sMe = this;
    }
 
    static PhoneApp getInstance() {
        return sMe;
    }
 
}

以上面代碼來看,繼承於Application 的PhoneApp 當作了phone進程的入口。

   public PhoneApp() {

       sMe = this;

    }該方法說明繼承於Application的類,必須可以被外部實例, 實際上在啓動過程中,如果有該入口,就是必須先實例該類的。Android 提供該部分接口,是爲了有些應用需要常駐內存。這些應用可能需要提供一些provider或service之類,但這些功能又必須先於Activity而存在,因而提供該入口讓java 的應用有個類似於main函數的入口,做些初始化的工作。

  

發佈了18 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章