本文主要對android4.4 RIL的telephony與modem的命令交互流程進行分析,當然本文不是重點介紹telephony。telephony涉及具體業務邏輯內容比較多,包括sim、dail、sms、network等等,以後會針對這些內容學習分析。 RIL在Android體系中的位置: ( ...
本文主要對android4.4 RIL的telephony與modem的命令交互流程進行分析,當然本文不是重點介紹telephony。
telephony涉及具體業務邏輯內容比較多,包括sim、dail、sms、network等等,以後會針對這些內容學習分析。
RIL在Android體系中的位置:
(A) 應用層發起訪問modem的請求
(B) RILD進程
(A) 應用層發起訪問modem的請求
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中的類RIL,提供了一系
列的介面給上層應用調用,以訪問modem。當然這些介面並不是直接給APP使用,而是由framework中sim、dail、
sms、network等相關服務調用。
如: 以查詢SIM卡狀態getIccCardStatus()為例,該API為UiccController模塊所調用:
完整的SIM卡請求log:
10-11 12:21:43.630 D/RILJ ( 1833): [3653]> GET_SIM_STATUS
10-11 12:21:43.630 D/RILC ( 1286): [0005]> GET_SIM_STATUS
10-11 12:21:43.630 D/RILC ( 1286): onRequest: GET_SIM_STATUS
10-11 12:21:43.630 D/ATC ( 1286): AT> AT+CPIN?
10-11 12:21:43.640 D/ATC ( 1286): AT< +CPIN: READY
10-11 12:21:43.640 D/ATC ( 1286): AT< OK
10-11 12:21:43.640 D/RILC ( 1286): [0005]< GET_SIM_STATUS {[app_type=1,app_state=5,perso_substate=2,aid_ptr=(null),app_label_ptr=(null),pin1_replaced=0,pin1=0,pin2=0],}
10-11 12:21:43.640 D/RILJ ( 1833): [3653]< GET_SIM_STATUS IccCardState {CARDSTATE_PRESENT,PINSTATE_UNKNOWN,num_apps=1,gsm_id=0{APPTYPE_SIM,APPSTATE_READY},cdma_id=8,ims_id=8}
發起請求:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
@Override public void getIccCardStatus(Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); /* 獲取一個RILRequest */ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); /* 參考log:[3653]> GET_SIM_STATUS */ send(rr); /* 發送請求 */ }
RILRequest.obtain是從記憶體池獲取一個RILRequest實例,並初始化:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
class RILRequest { static final String LOG_TAG = "RilRequest"; ... ... static RILRequest obtain(int request, Message result) { RILRequest rr = null; synchronized(sPoolSync) { /* 從記憶體池中取出一個RILRequest對象*/ if (sPool != null) { rr = sPool; sPool = rr.mNext; rr.mNext = null; sPoolSize--; } } if (rr == null) { /* 如果記憶體池中沒有,則實例化一個 */ rr = new RILRequest(); } rr.mSerial = sNextSerial.getAndIncrement(); /* 獲取串號,並且+1 */ rr.mRequest = request; /* 對應RIL_REQUEST_*請求 */ rr.mResult = result; rr.mParcel = Parcel.obtain(); /* 初始化一個Parcel,用於封包以發送給底層RILD */ if (result != null && result.getTarget() == null) { throw new NullPointerException("Message target must not be null"); } // first elements in any RIL Parcel rr.mParcel.writeInt(request); /* 每個RIL Parcel最開始也最基本的兩個元素 */ rr.mParcel.writeInt(rr.mSerial); return rr; /* 返回這個從記憶體池中獲取的實例 */ } ... ... String serialString() { //Cheesy way to do %04d StringBuilder sb = new StringBuilder(8); /* 創建一個StringBuilder實例用於操作字元串 */ String sn; long adjustedSerial = (((long)mSerial) - Integer.MIN_VALUE)%10000; sn = Long.toString(adjustedSerial); /* 把數值轉換成字元串 */ //sb.append("J["); sb.append('['); for (int i = 0, s = sn.length() ; i < 4 - s; i++) { sb.append('0'); } sb.append(sn); sb.append(']'); return sb.toString(); /* 轉換出來的字元串格式: [xxxx] */ } ... ... }
send(rr)發送請求到服務端:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private void send(RILRequest rr) { Message msg; ... ... msg = mSender.obtainMessage(EVENT_SEND, rr); /* 發送EVENT_SEND時間,時間參數為RILRequest */ acquireWakeLock(); /* 獲取wakelock,禁止進入休眠 */ msg.sendToTarget(); /* message從handler類獲取,從而可以直接向該handler對象發送消息。target就是創建message的handler */ }
實際上telephony無法直接與modem通訊,由於每個廠商的modem都不一樣,modem存在於系統中的方式
也不一樣,如:有的CPU晶元廠商的modem是以一個CP核的方式集成在基帶上(高通、展訊等),有的CPU芯
片(Exynos 4412等)需要通過串口/USB外接modem模塊,如:BC72 LTE模塊等。
send(rr)向RILD發送請求,這裡涉及一個進程間通信問題,而且Java側與C++側的進程通訊,當然這裡並沒
有用Android開發的朋友都熟悉的Bind,而是socket。
telephony/Java側RIL命令交互的處理,暫且稱為RILJ。
RILJ作為socket的客戶端,RILD(rild進程)作為服務端,後面會分析rild進程。
socket客戶端的創建:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { ... ... mSenderThread = new HandlerThread("RILSender"); /* 創建RILSender線程 */ mSenderThread.start(); Looper looper = mSenderThread.getLooper(); mSender = new RILSender(looper); ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { riljLog("Starting RILReceiver"); mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver"); /* 創建RILReceiver線程 */ mReceiverThread.start(); ... ... } ... ... } class RILReceiver implements Runnable { byte[] buffer; RILReceiver() { /* 構造時,分配一個數組 */ buffer = new byte[RIL_MAX_COMMAND_BYTES]; } @Override public void run() { /* 迴圈讀取從RILD返回或主動上報的數據 */ int retryCount = 0; try {for (;;) { LocalSocket s = null; LocalSocketAddress l; try { s = new LocalSocket(); /* 創建一個socket客戶端 */ l = new LocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED); s.connect(l); /* 連接伺服器 */ } ... ... } ... ... try { InputStream is = mSocket.getInputStream(); /* 迴圈讀取socket的數據 */ for (;;) { Parcel p; length = readRilMessage(is, buffer); /* 解析socket數據 */ if (length < 0) { // End-of-stream reached break; } p = Parcel.obtain(); /* 獲取一個Parcel */ p.unmarshall(buffer, 0, length); /* 讀取出來的就是之前序列化的byte數組,所以要進行一個反序列化操作 */ p.setDataPosition(0); /* 從buffer轉換到Parcel之後,需要將指針手動指向到最初的位置 */ //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes"); processResponse(p); p.recycle(); /* 數據處理完後,需要回收Parcel的記憶體 */ } } ... ... }
RILReceiver線程創建socket客戶端,連接服務端,然後進入等待服務端的processResponse消息處理迴圈,RILJ
接收到RILD回覆的response返回RIL請求的發起者,以getIccCardStatus(Message result)為例,processResponse(p)
會把DRILD的response返回給UiccController
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private static int readRilMessage(InputStream is, byte[] buffer) throws IOException { int countRead; int offset; int remaining; int messageLength; // First, read in the length of the message offset = 0; remaining = 4; do { countRead = is.read(buffer, offset, remaining); /* 讀出消息的4位元組長度 */ if (countRead < 0 ) { Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message length"); return -1; } offset += countRead; remaining -= countRead; } while (remaining > 0); messageLength = ((buffer[0] & 0xff) << 24) /* 獲取長度 */ | ((buffer[1] & 0xff) << 16) | ((buffer[2] & 0xff) << 8) | (buffer[3] & 0xff); // Then, re-use the buffer and read in the message itself offset = 0; remaining = messageLength; do { countRead = is.read(buffer, offset, remaining); /* 讀取剩餘的數據 */ if (countRead < 0 ) { Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message. messageLength=" + messageLength + " remaining=" + remaining); return -1; } offset += countRead; remaining -= countRead; } while (remaining > 0); return messageLength; } private void processResponse (Parcel p) { int type; type = p.readInt(); /* 從RILD返回的數據第一個位元組,表示請求的返回類型:RESPONSE_UNSOLICITED/RESPONSE_SOLICITED */ if (type == RESPONSE_UNSOLICITED) { processUnsolicited (p); /* 主動上報 */ } else if (type == RESPONSE_SOLICITED) { RILRequest rr = processSolicited (p); /* 普通請求對應的同步上報 */ if (rr != null) { rr.release(); /* 釋放對應的RILRequest記憶體和wakelock */ decrementWakeLock(); } } }
RILD的response一般有兩種,一種是RILJ普通請求,RILD對RILJ請求的response (RESPONSE_SOLICITED),另一種是RILD主動上報的
response (RESPONSE_UNSOLICITED), processResponse (Parcel p)分別對這兩種情況的response進行處理。
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private RILRequest processSolicited (Parcel p) { int serial, error; boolean found = false; serial = p.readInt(); /* 串號,也就是token */ error = p.readInt(); /* 錯誤碼 */ RILRequest rr; rr = findAndRemoveRequestFromList(serial); /* 根據taken取出對應的RILRequest */ ... ... */ case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break; ... ... if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, ret, null); /* 把rr.mResult存到AsyncResult.userObj,並把rr.mResult.obj轉換為AsyncResult */ rr.mResult.sendToTarget(); /* msg發送到對應的target(Handler) */ } ... ... } private Object responseIccCardStatus(Parcel p) { IccCardApplicationStatus appStatus; ... ... appStatus = new IccCardApplicationStatus(); ... ... return cardStatus; }
回到剛纔send(rr),send(rr)並不是直接發送到socket服務端RILD,而是通過一個Message發送到RILSender線程,
在handleMessage中,把請求發到socket服務端RILD。
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
class RILSender extends Handler implements Runnable { /* 繼承Handler,實現Runnable */ public RILSender(Looper looper) { super(looper); } ... ... //***** Handler implementation @Override public void handleMessage(Message msg) { /* 繼承Handler的handleMessage */ RILRequest rr = (RILRequest)(msg.obj); /* Maessage中攜帶的RILRequest對象 */ RILRequest req = null; switch (msg.what) { case EVENT_SEND: /* 發送RIL請求事件 */ try { LocalSocket s; s = mSocket; /* RILReceiver中創建的用於與RILD通訊的socket */ ... ... synchronized (mRequestList) { /* 多線程保護操作mRequestList */ mRequestList.append(rr.mSerial, rr); /* 把接受到的RILRequest和對應的串號,存到mRequestList數據 */ } byte[] data; data = rr.mParcel.marshall(); /* 把Parcel中的數據轉換為byte數據 */ rr.mParcel.recycle(); /* Parcel的記憶體回收 */ rr.mParcel = null; ... ... // parcel length in big endian dataLength[0] = dataLength[1] = 0; /* RIL請求包的大小為4個位元組 */ dataLength[2] = (byte)((data.length >> 8) & 0xff); dataLength[3] = (byte)((data.length) & 0xff); //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes"); s.getOutputStream().write(dataLength); /* 把包大小和包數據發送出去 */ s.getOutputStream().write(data); } catch (IOException ex) { Rlog.e(RILJ_LOG_TAG, "IOException", ex); req = findAndRemoveRequestFromList(rr.mSerial); /* 如果出現異常,則把串號對應的RILRequest從mRequestList中刪除 */ // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); decrementWakeLock(); } } ... ... } } }
(B) RILD進程
RILD作為一個獨立的進程,telephony與modem之間的通訊通道。抽象出一些介面以適配不同的modem廠商,無需關心具體的
硬體操作,或者以哪種形式存存在於系統(modem作為CP集成於CPU或CPU通過串口/USB連接,如: BC72 LTE模塊)。因為這些介面
由廠商去實現具體的硬體操作細節,這些介面都在libreference-ril中,在Android中使用BC72 LTE模塊,只要移植
libreference-ril就行。
1. RILD的啟動
RILD有init進程直接啟動,啟動後就監聽RILJ客戶端,等待RILJ連接請求。
device/samsung/smdk4x12/conf/init.smdk4x12.rc
service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root
hardware/ril/rild/rild.c為RILD進程入口:
hardware/ril/rild/rild.c
int main(int argc, char **argv) { ... ... dlHandle = dlopen(rilLibPath, RTLD_NOW); /* 打開/system/lib/libreference-ril.so */ if (dlHandle == NULL) { RLOGE("dlopen failed: %s", dlerror()); exit(-1); } RIL_startEventLoop(); /* 創建eventLoop線程, 在ril_event_loop()中監聽多路IO的事件,如主動喚醒事件(pipe)、RILJ的請求等 */ /* 獲取/system/lib/libreference-ril.so中RIL_Init函數指針 */ rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init"); if (rilInit == NULL) { RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath); exit(-1); } if (hasLibArgs) { rilArgv = argv + i - 1; argc = argc -i + 1; } else { static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv; property_get(LIB_ARGS_PROPERTY, args, ""); argc = make_argv(args, rilArgv); } // Make sure there's a reasonable argv[0] rilArgv[0] = argv[0]; funcs = rilInit(&s_rilEnv, argc, rilArgv); /* 初始化Vender RIL */ RIL_register(funcs); /* 註冊RIL */ ... ... }
hardware/ril/libril/ril.cpp
extern "C" void RIL_startEventLoop(void) { ... ... ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL); /* 創建eventLoop線程 */ ... ... } static void * eventLoop(void *param) { int ret; int filedes[2]; ril_event_init(); /* 初始化事件鏈表,timer_list,pending_list, watch_table */ pthread_mutex_lock(&s_startupMutex); s_started = 1; pthread_cond_broadcast(&s_startupCond); pthread_mutex_unlock(&s_startupMutex); ret = pipe(filedes); /* 創建一個pipe,用於每次添加一個新事件時,喚醒selet()返回,更新fd_set使select監聽新的事件 */ if (ret < 0) { RLOGE("Error in pipe() errno:%d", errno); return NULL; } s_fdWakeupRead = filedes[0]; /* filedes[0]用於讀pipe, filedes[1]用於寫pipe */ s_fdWakeupWrite = filedes[1]; fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); /* 以非阻塞的方式讀pipe */ ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, /* 讀pipe描述符綁定到s_wakeupfd_event事件,指定回調processWakeupCallback */ processWakeupCallback, NULL); rilEventAddWakeup (&s_wakeupfd_event); /* 添加s_wakeupfd_event事件到watch_table,更新readFds集合,使select監聽該事件,並觸發該事件 */ // Only returns on error ril_event_loop(); /* 進入多路IO事件監聽迴圈 */ RLOGE ("error in event_loop_base errno:%d", errno); // kill self to restart on error kill(0, SIGKILL); return NULL; }
main函數主要啟動eventLoop線程,在ril_event_loop()中監聽多路IO的事件,如主動喚醒事件(pipe)、RILJ的請求等,
註冊vendor RIL介面(libreference-ril)
註意這裡pipe的主要作用是喚醒select返回,因為每次動態的添加一個事件,都要更新readFds集合,方便select監聽
集合中新的IO。
rilEventAddWakeup()添加新事件後,都會觸發select返回
hardware/ril/libril/ril.cpp
static void rilEventAddWakeup(struct ril_event *ev) { ril_event_add(ev); /* 添加事件 */ triggerEvLoop(); /* 觸發事件, 每添加一個事件,都通過寫pipe喚醒select,以更新多路IO集合,使能夠監聽該事件 */ }
hardware/ril/libril/ril_event.cpp
void ril_event_add(struct ril_event * ev) { dlog("~~~~ +ril_event_add ~~~~"); MUTEX_ACQUIRE(); for (int i = 0; i < MAX_FD_EVENTS; i++) { if (watch_table[i] == NULL) { watch_table[i] = ev; /* 將新事件添加到watch_table */ ev->index = i; dlog("~~~~ added at %d ~~~~", i); dump_event(ev); FD_SET(ev->fd, &readFds); /* 更新readFds集合 */ if (ev->fd >= nfds) nfds = ev->fd+1; /* 更新nfds */ dlog("~~~~ nfds = %d ~~~~", nfds); break; } } MUTEX_RELEASE(); dlog("~~~~ -ril_event_add ~~~~"); }
hardware/ril/libril/ril.cpp
static void triggerEvLoop() { int ret; if (!pthread_equal(pthread_self(), s_tid_dispatch)) { /* trigger event loop to wakeup. No reason to do this, * if we're in the event loop thread */ do { ret = write (s_fdWakeupWrite, " ", 1); /* 向pipe寫入一個" ",以喚醒select */ } while (ret < 0 && errno == EINTR); } }
在ril_event_loop()接收到事件或socket客戶端RILJ發過來的請求後,firePending()根據事件請求,調用相應的處理函數
hardware/ril/libril/ril_event.cpp
void ril_event_loop() { int n; fd_set rfds; struct timeval tv; struct timeval * ptv; for (;;) { // make local copy of read fd_set memcpy(&rfds, &readFds, sizeof(fd_set)); if (-1 == calcNextTimeout(&tv)) { /* 計算timer_list鏈表中每個事件對應的超時時間 */ // no pending timers; block indefinitely dlog("~~~~ no timers; blocking indefinitely ~~~~"); ptv = NULL; } else { dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec); ptv = &tv; } printReadies(&rfds); n = select(nfds, &rfds, NULL, NULL, ptv); /* 等待readFds集合中的事件喚醒 */ printReadies(&rfds); dlog("~~~~ %d events fired ~~~~", n); if (n < 0) { if (errno == EINTR) continue; RLOGE("ril_event: select error (%d)", errno); // bail? return; } // Check for timeouts processTimeouts(); /* 檢查timer_list鏈表中是否有事件已經超時 */ // Check for read-ready processReadReadies(&rfds, n); /* 從watch_table中取出監聽到的事件, 並添加到pending_list鏈表 */ // Fire away firePending(); /* 從pending_list依次取出事件,並執行該事件的回調 */ } } static void processTimeouts() { dlog("~~~~ +processTimeouts ~~~~"); MUTEX_ACQUIRE();