Android 4.4 Kitkat Phone工作流程淺析(四)__RILJ工作流程簡析

來源:互聯網
上載者:User


本文來自http://blog.csdn.net/yihongyuelan 轉載請務必註明出處
       本文代碼以MTK平台Android 4.4為分析對象,與Google原生AOSP有些許差異,請讀者知悉。
前置文章:
《Android 4.4 Kitkat Phone工作流程淺析(一)__概要和學習計劃》

《Android 4.4 Kitkat Phone工作流程淺析(二)__UI結構分析》

《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》

       通過上一篇文章《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》,我們知道在 Android 4.4 中,撥打到電話最終調用到了RIL.java中,並使用send()方法繼續向下傳遞。本文主要目的是簡單介紹RILJ工作流程,為後續MT流程分析打下基礎。

RIL概述

       RIL 即 Ridio Interface Layer縮寫,無線通訊介面層,Android 中的實現分為RILJ和RILC兩部分。RILJ屬於Framework層中的Java部分( 後文使用RILJ指代RIL.java ),RILC屬於HAL層中的C/C++部分(也就是rild)。RIL負責將AP層使用者的通話控制資訊傳遞給BP層Modem端,同時Modem端也會將相關處理結果返回給AP層。另外Modem狀態有改變時也會主動上報給RIL層,再逐步向上傳遞並最終通過介面顯示出來。本文僅簡單介紹RILJ部分,詳細分析請參看其他大神的分析。

RILJ與RILC以及Modem之間關係1:


圖1

MO/MT在RILJ中執行流程:1. MO在RILJ中流程:

       當使用者執行MO(去電)時,通過層層判斷之後,系統會將相關資訊傳遞到RILJ中,封裝好後使用AT指令的方式發送到Modem端,最終由Modem端發起通話請求。在通話接通後Modem端會返回相關AT指令資訊給RIL層,RIL層再向上反饋。

整個MO(去電)流程可以簡單的歸結為兩個步驟

(1). ( Request ,  Response) ,即向RILC發起Request,再由RILC向Modem發送相關AT指令,等待Modem處理並反饋結果,如DIAL操作;

(2). (Response),即在對方接通之後,Modem會將狀態資訊反饋到RILJ中;

2. MT在RILJ中流程:

       當Modem端收到來電資訊時,會將相關來電資訊通過AT指令發送給RILC,再通過RILC使用socket發送給RILJ,逐層向上傳遞,最終顯示來電響鈴介面。

MT(來電)與MO(去電)的第二步相同,通過( Response ),即Modem端主動上報狀態改變資訊;

我們先來看一下RILJ 的基本組成以及主要功能,圖2為RILJ核心類圖:


圖2

通過我們可以看到,RILJ繼承自BaseCommands實現了CommandsInterface介面,RILJ包含了三個內部類:RILRequest、RILReceiver、RILSender,分別對應mRequestsList、mSender、mReceiver三個對象。

RILJ中涉及通話控制和訊息處理的關鍵方法:

通話控制類:

dial(),負責建立撥號RILRequest對象;

acceptCall(),負責建立接聽RILRequest對象;

rejectCall(),負責建立拒接RILRequest對象;

以上方法最終會通過RILSender並使用socket方式向RILC發起相關請求。

訊息處理類:

processUnsolicited(),負責處理UnSolicited反饋資訊;

processSolicited(),負責處理Solicited反饋資訊;

       這些訊息來自RILC的socket反饋,在上一篇MO(去電)流程分析中,dial最終會調用到RIL.java中的RILSender對象的相關方法,向RILC繼續傳遞撥號資訊,而MT(來電)時Modem會將相關資訊發送到RILC,RILC再將資訊通過socket反饋給RILJ,在RILJ中通過RILReceiver接收並開始處理。RILSender負責通過socket向RILC發送RILRequest資訊,RILReceiver負責以socket方式從RILC接收Response資訊。當RILJ從RILC接收到相關資訊時,會逐步向上反饋,這些資訊分為兩類:

1. Solicited Response

        請求返回訊息,這類訊息是上層主動請求Modem再由其返回的訊息,這些訊息由RILC上報給RILJ。比如當我們執行MO(去電)時,會通過RILSender發送dial類型的Request給RILC,RILC再傳遞給Modem端,再由Modem端發起通話請求。待對方接通之後Modem將相關資訊發送到RILC,RILC將結果訊息反饋給RILJ,此時由RILReceiver來接收並處理訊息,這種訊息就是Solicited訊息。

       可以簡單的理解為( Request , Response )這樣成對出現,此時RILC向RILJ返回的Respone就是Solicited訊息。

2. UnSolicited Response

       主動返回訊息,這類訊息由Modem端主動上報。比如當有MT(來電)時,Modem會接收到資訊並向RILC發送來電訊息,RILC將來電資訊再發送給RILJ,這類沒有request但由Modem主動上報資訊就是UnSolicited資訊。

       可以簡單的理解為Modem端主動給RILC,再由RIC上報給RILJ的( Response )訊息,這類訊息就是UnSolicited訊息。

無論是 Solicited 還是UnSolicited Respone 訊息,在RILJ中都是有RILReceiver來負責接收並處理,整個RILJ的工作可以簡單的用圖3來描述:


圖3

RILJ包含了RILSender和RILReceiver,其中RILSender負責發送Request給RILC,RILReceiver負責接收Solicited和UnSolicited的Response資訊。

RILJ工作流程

RILJ的工作內容主要是負責向RILC發起Request,以及從RILC接收Response。主要涉及三個內部類:RILRequest、RILSender、RILReceiver,主要工作包含以下三大內容:

1. RILRequest的構造;

       RILRequest是RILJ的內部類,我們可以把RILRequest看成一個打包類,將相關資訊進行加工處理後打包成一個規範的RILRequest並使用RILSender發送給RILC。在RILRequest類中最重要的方法莫過於obtain了,該方法完成了打包工作。我們在MO(去電)流程分析中有看到調用RILJ中的dial方法,並在dial方法中看到了RILRequest的打包代碼,如下:

RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result, mySimId);
這裡再去看一下obtain方法的實現:

    static RILRequest obtain(int request, Message result, int simId) {        RILRequest rr = null;        synchronized(sPoolSync) {            if (sPool != null) {                rr = sPool;                sPool = rr.mNext;                rr.mNext = null;                sPoolSize--;            }        }        if (rr == null) {            rr = new RILRequest();        }        //這裡跟Android原生不同,這裡多了一個參數simId用於MTK雙卡手機標識哪一張SIM卡        //static int[] sNextSerial = {0, 0, 0, 0};這裡後面會從log中看到如"[233]"的編號        //serial是Request的編號並且是唯一的        synchronized(sSerialMonitor) {            rr.mSerial = sNextSerial[simId]++;        }        //請求類型        rr.mRequest = request;        rr.mResult = result;//請求結果 Message對象        rr.mParcel = Parcel.obtain();        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);        rr.mParcel.writeInt(rr.mSerial);        return rr;    }

2. RILSender發送Request;

       在Request構造完成之後我們在dial方法中看到最終調用了send(rr)方法,這裡就用到了RILSender,RILSender也是RILJ中的內部類,通過查看send方法可以知道:

    private void    send(RILRequest rr) {        Message msg;        if (mSocket == null) {            rr.onError(RADIO_NOT_AVAILABLE, null);            rr.release();            return;        }        //RILSender整合Handler因此會跳轉到相應的handleMessage方法中        msg = mSender.obtainMessage(EVENT_SEND, rr);        acquireWakeLock();        msg.sendToTarget();    }

通過前面的核心類圖我們可以知道,RILSender整合自Handler,因此這裡會跳轉到RILSender的handleMessage方法中,並執行case EVENT_SEND,代碼如下:

case EVENT_SEND:    boolean alreadySubtracted = false;    try {    LocalSocket s;    //擷取LocalSocket,並對socket進行判斷是否正常    s = mSocket;    //... ...省略部分代碼    //將Request添加到鏈表中    synchronized (mRequestList) {            mRequestList.add(rr);    }    //... ...省略部分代碼    //定義socket發送需要的byte數組    byte[] data;    //擷取資料    data = rr.mParcel.marshall();    //... ...省略部分代碼    //向socket寫入資料    s.getOutputStream().write(dataLength);    s.getOutputStream().write(data);    }    //... ...省略部分代碼break;

這裡使用了socket的方式將撥號請求發送給了RILC,最後又RILC向Modem端傳遞。在完成以上步驟之後dial流程就走到了RILC中,再由RILC向Modem端發出dial的AT指令。

3. RILReceiver接收Response;

       當MO(去電)接聽以及MT(來電)時,Modem端會將相關資訊通過RILC發送給RILJ,負責接收的正式RILReceiver,RILReceiver也是RILJ的內部類,該類實現了Runnable介面,因此實際啟動是以Thread的方式啟動的。RILReceiver在PhoneProxy的構造方法中通過調用startRilReceiver啟動,這裡是MTK改動過,原生是直接在RIL.java的構造方法中啟動線程。

RILReceiver主要完成兩項工作:

(1). 維護local socket的串連;

(2). 阻塞local socket並處理RILC的Response ( Solicited 和 UnSolicited );

RILReceiver啟動之後會建立local socket並阻塞,代碼如下:

        @Override        public void        run() {            //... ...省略部分代碼            try {                for (;;) {                //... ...省略部分代碼                //建立並維護 Local Socket                try {                    s = new LocalSocket();                    l = new LocalSocketAddress(socketRil,                            LocalSocketAddress.Namespace.RESERVED);                    s.connect(l);                } catch (IOException ex){                //... ...省略部分代碼                //當串連失敗後休眠4s並繼續嘗試串連                }                //... ...省略部分代碼                try {                    InputStream is = mSocket.getInputStream();                    for (;;) {//阻塞                        Parcel p;                        length = readRilMessage(is, buffer);                        if (length < 0) {                            // End-of-stream reached                            break;                        }                        p = Parcel.obtain();                        p.unmarshall(buffer, 0, length);                        p.setDataPosition(0);                        //處理Respone訊息(Solicited和UnSolicited)                        processResponse(p);                        p.recycle();                    }                }                //... ...省略部分代碼        }

那麼通過以上分析我們可以簡單的將RILJ的工作流程總結為:打包,發送,接收三個步驟。在RILReceiver中接收到RILC的socket資訊之後,使用processResponse進行處理,進而分為processUnsolicited()和processSolicited()方法。

processSolicited()方法主要完成以下三件事:

(1). 根據Response中的request serial編號找到RequestList中的對象並移除;

       因為Solicited的Request是一一對應的,當處理完本次Response之後不再需要改Request對象,因此這裡會移除,使用方法findAndRemoveRequestFromList()。

(2). 根據request的類型使用responseXXX方法處擷取從RILC中返回的資訊;

       這裡會有很多request類型,對應的responseXXX方法也有很多,比如responseInts()、responseVoid()、responseString()、responseOperator()等等,通過這些方法取出Parcel對象中的內容,該對象中包含了Request需要查詢的資訊。

(3). 將第2步擷取到的資訊儲存到Message中並觸發回調;

執行代碼如下:

if (rr.mResult != null) {    AsyncResult.forMessage(rr.mResult, ret, null);    rr.mResult.sendToTarget();}
我們知道RILRequest類中有定義mResult為Message類型,在obtain方法執行的時候已經對mResult進行了賦值,這裡的Message就是從GSMCallTracker中傳遞過來的,有興趣的童鞋自己追蹤一下吧,注意看清楚dial中的參與即可。也就是說通過分析我們知道processSolicited()處理完成會攜帶相關資訊跳轉到GSMCallTracker中繼續執行。

processUnsolicited()方法主要以下兩件事:

(1). 根據response訊息的類型使用responseXXX方法處擷取從RILC中返回的資訊;

這一步和processSolicited()方法的第2步類似,並將從Parcel對象中擷取到的資料儲存到局部變數Object類型的對象ret中。

(2). 使用notifyRegistrants()方法將相關語音總機到訂閱者( Subscribe );

因為這裡使用的是觀察者模式( Publisher-Subscribe ),相關訂閱者會根據擷取到不同的資訊作出與之對應的反應。

       對於MO(去電)而言,整個過程既包含了processSolicited()也包含了processUnsolicited()。為什麼這麼說呢?當我們執行dial操作後,會將相關資訊發送到Modem端,此時執行的步驟是DIAL,Modem端發起DIAL成功後返回給RILJ,這個步驟就是( Request , Respone )。之後如果對方接通了那麼Modem會將相關資訊反饋給RILJ,這個步驟即( Respone ),通過Radio Log可以看到:

(a). 撥號Request和Respone

D/RILJ    (  966):  RIL(1) :[0253]> DIAL
D/RILJ    (  966):  RIL(1) :[0253]< DIAL 

(b). 返回MO接通的Respone

V/RILJ    (  966):  RIL(1) :[UNSL RIL]< UNSOL_CALL_PROGRESS_INFO {1, 6, 0, 1, 0, 0, 13800138000, 145, }

至於MO接通後返回資訊裡面的參數這裡不詳細解釋,主要是AT指令中攜帶的資訊,不同硬體平台會有一些自己的定製。對於Request和Respone並不一定是連續出現在Log中的,我們只需要查看其唯一的serial編號即可找到Request對應的Respone。另外,在RILJ的Log中我們可以根據大於(>)符號和小於(<)符號來判斷是Request還是Response,即:

(I). 大於符號(>)對應Request,表示RILJ向RILC發送請求;

(II). 小於符號(<)對應Response,表示RILC向RILJ反饋資訊;

       前面我們說過Solicited資訊是成對出現的,通過log也能證明這一點,平時查看Log可以根據request的serial編號來找到對應的response。在完成以上撥號操作之後Solicited資訊就執行完畢了,接下來是等待對方接通,若對方接通則Modem會收到相應的指令並向RILC傳遞接通訊息。該資訊由Modem主動上報,因此屬於UnSolicited資訊,由processUnsolicited()方法來處理。

總結

       RILJ的主要作用是將通話控制資訊使用socket傳遞給RILC,RILC再使用AT指令傳遞給Modem端;RILC通過socket返回的Modem處理結果給RILJ並通知上層應用;可以說RILJ在Android Telephony結構中有著承上啟下的作用。

RILJ的主要工作內容可以概括為以下三點:

1. 構造打包RILRequest;

2. RILSender通過socket向RILC發起request;

3. RILReceiver通過socket結構RILC反饋response;

RILReceiver接收到的訊息分為兩類:

1. Solicited Response,與之對應的處理方法是processSolicited();

2. UnSolicited Response,與之對應的處理方法是processUnsolicited();




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.