基于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上实现。
                                             

你可能感兴趣的:(android,通讯,RIL)