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函數的入口,做些初始化的工作。