基於Android RIL層實現來電攔截的技術原理(一)

引入
         目前市面上,Android上的防騷擾類應用非常多,比如騰訊手機管家、360手機衛士、金山手機衛士等。由於受Android OS設計框架,他們的來電攔截實現,都是通過接受com.android.phone進程所發送的廣播而實現。具體的實現方案,網上有很多教程,在此不再敘述。
     採用上述的方案實現來電攔截,存在一個先天性的技術缺陷——每當有來電時,都會跳到系統來電UI,而且還伴有短暫的鈴聲,之後纔會被掛斷。究其原因,主要是因爲Android RIL的JAVA的響應邏輯,在com.android.phone實現的,也就是說,當有來電時,會先經由com.android.phone處理,之後纔對外發送廣播。因此,無論技術處理上如何快速,前面所說的技術缺陷也是無法解決的。當然,如果在有root的情況下,就有完善的實現方案,也就是接下來準備介紹的方案——基於Android RIL層的來電攔截方案。

前提
     本次的技術可行性分析,是建立在已經成功實現在Android上進行so和dex注入的基礎上。

原理
     通過分析來電在RIL層的消息傳遞流程, 並尋流程中的所有“連接點”。然後選擇最適合的“連接點”進行代碼注入。所謂“連接點”有很多表現形式,總結起來,主要有兩種:一種是方法重寫(類與類之間的關係);一種包裝(實體與實體的關係)。當然這兩種方式,都是代碼級別的,如果跳出代碼級別,那在JAVA每一個函數的調用,都可以是一個“連接點“(比如通過修改JNI的methodID的結構體中native方法的指針地址等)。
     而本次技術方案的實現,則同時使用了上面所介紹的兩種方式,下面有詳細介紹。

分析
     網上關於Android RIL原理的介紹,也有很多,在此推薦鏈接http://www.360doc.com/content/11/0221/10/474846_94752600.shtml
     在瞭解了RIL的工作原理之後,再來了解RIL層的來電攔截,就非常容易了。在JAVA層,由於由com.android.phone通過localsocket(http://www.linuxidc.com/Linux/2012-07/65506.htm)跟rild進行通訊。當有來電的時候,rild會通知com.android.phone,下面是關於XXXX_CALL_RING的通訊流程分析(JAVA層):

RIL$RILReceiver.run
RIL.readRilMessage
RIL.processResponse
RIL.processUnsolicited
Registrant.notifyRegistrant
Registrant.internalNotifyRegistrant
Handler.sendMessage
Handler.handleMessage
PhoneBase.handleMessage
PhoneBase.notifyIncmoingRing
RegistrantList.notifyRegistrants
RegistrantList.internalNotifyRegistrants
Registrant.internalNotifyRegistrant
Handler.sendMessage
Handler.handleMessage
CallManager.handlerMessage
RegistrantList.notifyRegistrants
RegistrantList.internalNotifyRegistrants
Registrant.internalNotifyRegistrant
Handler.sendMessage
Handler.handleMessage
CallNotifier.handleMessage
Ringer.ring(響鈴)
...

     通過源碼分析,其中紅色部分的調用堆棧,是framework層的;
     而藍色部分,則package/phone層的;
     其中加粗加下劃線部分,是各層次消息傳遞的關鍵“連接點”邏輯,主要是通過嵌套使用Handler的實現。
     因此,要實現來電攔截,在這三個環節的任意一個,插入判斷邏輯都可以。但還需要結合框架本身的設計出發考慮。由於PhoneBase是各種phone(GSMPhone, CDMAPhone等類)的基類,因此在第一個環節進行“注入”,並不是好的控制點,因爲需要實現多處“注入”,加大了風險和不確定性。而在第二個和第三個都只需要注入一次即可,但第三個更接近應用層,因此API更加穩定,而且兼容性更好。最後,我們選擇第三個點進行注入,即CallNotifier這個Handler。
     
     下面是具體實現的代碼片斷:
     
     class ProxyHandler extends Handler {
          private Handler mInnter;

          public  ProxyHandler(Handler h){
               mInnter = h;
          }

          @Override
           public void handleMessage(Message msg) {
               //做愛做的事
               // ......
               // ......

               mInnter.handlerMessage(msg);
           }
     }

     CallManager instance = CallManager.getInstance();

     //通過反射,拿到其字段mIncomingRingRegistrants
     RegistrantList mIncomingRingRegistrants = instance.mIncomingRingRegistrants;
     for(int i=0; i<mIncomingRingRegistrants.size(); i++){
          Registrant item = mIncomingRingRegistrants.get(i);
          
          Handler handler = item.getHandler();
          //通過反射,拿到其字段refH
          item.refH = new WeakReference(new ProxyHandler(handler)); //完成注入
     }
     

需要注意
     如果採用了本方案的來電攔截方式,屬於是“事前處理”,跟原來的方式完全不一樣了(原來是無序廣播的方式,各個監聽都可以接收,屬於“事後處理”)。所以,攔截的流程上會有諸多變化,也有各種細節需要考慮。

後話
     本方案是在java層實現的,因此還可以非常方便的使用Android SDK所提供的各種API。另外,對於IPC通訊也非常方便(因爲com.android.phohe已經有完整的環境)。而除了在java層上實現之外,還可以通過本地庫實現。原理很簡單,就是在com.android.phone與 rild之間,加入“中間人”,做中轉代理。這個方法,之前也有人實現過,並做出了DEMO。但由於更偏向於低層,很多協調的解釋需要看源碼,不如在framework層,可以直接拿到解釋後的數據。由於我們的目的是攔截來電時不希望跳轉到來電界面以及響一聲,更偏向於App層面的邏輯,所以我更偏向於在framework上實現。
                                             
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章