要说拦截Android系统来电,就不得不说起在低版本的时候Android提供给开发者使用的一个方法:endCall(),但由于谷歌后来考虑到对于一部手机来说,最重要的功能就是打电话了,如果这个功能随随便便就被人屏蔽了,安全性太差,所以在高版本的Android将这个方法屏蔽了,不再在TelephoneManager中暴露这个方法。


那么我们下面的目标就是要想办法调用到这个方法,当然首先我们还是需要实现一个广播接收者,来接收电话状态改变的广播,这里使用在服务中动态注册广播接收者的方法来实现,主要好处在于便于控制广播接收者的生命周期,同时也能在权限值相同的情况下比静态注册优先级更高


在服务的onCreate()方法中注册广播接收者:

private static final String PHONE = "PHONE";private static final String BOTH = "BOTH";private static final String SMS = "SMS";private TelephonyManager tm;private BlacklistDao dao;private inCommingCallReceiver callReceiver;private PhoneStateListener listener;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);dao = new BlacklistDao(this, 1);callReceiver = new inCommingCallReceiver();IntentFilter filter = new IntentFilter();filter.addAction("android.intent.action.PHONE_STATE");filter.addAction("android.provider.Telephony.SMS_RECEIVED");filter.setPriority(Integer.MAX_VALUE);listener = new PhoneStateListener() {private BlacklistItem blacklistItem;@Overridepublic void onCallStateChanged(int state, String incomingNumber) {super.onCallStateChanged(state, incomingNumber);switch (state) {case TelephonyManager.CALL_STATE_RINGING:// 如果是来电的时候blacklistItem = dao.queryItem(incomingNumber);if (blacklistItem != null) {String type = blacklistItem.getType();if ((BOTH.equals(type) || PHONE.equals(type))) {System.out.println("挂断电话");hangUpCallFromBlacklist(incomingNumber);//挂断电话的方法}}break;default:break;}}};registerReceiver(callReceiver, filter);// 注册广播接收者}

在服务的onDestroy()方法中取消注册广播接收者:


@Overridepublic void onDestroy() {super.onDestroy();System.out.println("关闭黑名单服务");unregisterReceiver(callReceiver);// 取消监听tm.listen(listener, PhoneStateListener.LISTEN_NONE);// listener = null;}


内部类广播接收者,用于接收电话状态改变的广播:

/** * 监听来电 *  * @author Alex *  */private class inCommingCallReceiver extends BroadcastReceiver {private BlacklistItem blacklistItem;@Overridepublic void onReceive(Context context, Intent intent) {if ("android.intent.action.PHONE_STATE".equals(intent.getAction())) {// 如果收到的是电话状态的变化tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);}}}



下面我们重点来看hangUpCallFromBlacklist(incomingNumber);这个实现挂断来电的方法,其中incomingNumber是来电的电话号码。

按照我的惯例,还是从安卓系统的源码入手,由于endCall方法原来是在TelephoneManager中的,所以我们不妨从TelephoneManager的实例化方法getSystemService入手:


高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第1张图片



我们可以发现这个方法是定义在ContextWrapper中的,而ContextWrapper又是继承自Context,我们不妨进入到Context的源码中去一探究竟:




在Context中,我们只找到了一个getSystemService的抽象方法,那么如何去找实现方法呢,在java中,抽象类的实现类一般名字都是在抽象类名后面加上一个"Impl",于是我们去搜索源码中的ContextImpl.java,找到之后打开发现:




我们发现getSystemService实际上返回了一个ServiceFetcher对象的一个getService方法的结果,我们来看看ServiceFetcher的getService方法:


高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第2张图片



在这里我们可以看到,在定义了一个static块中,注册了很多不同的service服务,而这些gerService方法都是由ServiceManager来调用的,返回值是一个IBinder对象。接下来我们可以在文件的导入包的部分找到ServiceManager的位置是在android/os下的:




由于getService返回的是一个IBinder对象,我们只要找到这个getService方法的实现,就可以传入TELEPONY_SERVICE从而拿到真正的TelephonyManager所代理的那个远程服务绑定对象,从而调用隐藏在其中的endCall方法。

幸运的是,在ServiceMnager.java中,我们找到了getService方法的实现:


高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第3张图片

高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第4张图片


我们可以发现,ServiceManager这个类也是一个隐藏类,我们无法在我们的代码中直接拿到这个类来调用其中的getService方法来获取IBinder对象,那么我们要如何做呢?

这里就只有使用反射的方法来处理:


Class clazz = CallSmsSafeService.class.getClassLoader().loadClass("android.os.ServiceManager");Method method = clazz.getMethod("getService", String.class);IBinder binder = (IBinder) method.invoke(null, TELEPHONY_SERVICE);


通过上面的反射做法,我们拿到了对应于TelephonyManager的IBinder对象,下面我们需要做的利用aidl来调用远程方法,既然是使用的TelephonyManager的IBinder对象,我们再进入到TelephonyManager的源码中去看看:


高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第5张图片


我们发现,在TelephonyManager中,类似于getCallState()这类的方法基本都返回的是getITelephony()的返回值调用的方法,那么这个getITelephy()是什么呢:




我们发现,返回的实际上是一个ITelephony对象,而且是以一种调用远程服务方法的形式返回的;

我们在文件的头部找到ITelephony的位置:




打开上面的目录,我们发现ITelephony是一个aidl文件,进入其中,我们可以找到endCall方法:




由于在ITelephony.aidl的头部有如下信息:


高版本Android如何利用反射调用系统隐藏的远程服务拦截来电_第6张图片


我们想要通过aidl来调用远程服务ITelephony的方法endCall(),我们需要将Telehpony.aidl拷贝到我们工程中新建的com.android.internal.telephony包中,同时将android.telephony.NeighboringCellInfo.aidl文件拷贝到工程中新建的android.telephony包中,这样在gen目中下就会自动生成一个对应的ITelephony.java文件。至此,我们就可以使用下面的语句来调用远程服务的endCall方法:


ITelephony.Stub.asInterface(binder).endCall();


最后,不要忘记在清单文件中加入对应的权限


              



更多相关文章

  1. android下自定义字体的使用方法
  2. [Android Studio系列(三)]Android Studio 编译、同步慢的解决方
  3. android 方法数越界问题 65536/64k(方法数超过65535)
  4. Android Excel 解析 xls 和 xlsx,方法也可以很简单
  5. Android P (4)一种绕过Android P上非SDK接口限制的简单方法
  6. Android OTA包重新签名的方法
  7. 【Android语音合成TTS】百度语音接入方法,和使用技巧详解
  8. 通过wifi连接android设备的方法
  9. Android Studio中新建assets文件的两种方法

随机推荐

  1. View的工作原理(Android开发艺术探索读书
  2. Android(安卓)源码分析实战 - 把代码写得
  3. Android(安卓)Dialog种类大全,让Activity
  4. setDefaultKeyMode 用法介绍
  5. AndroidStudio快捷键
  6. Android简易实战教程--第三十一话《自定
  7. Wayland中的跨进程过程调用浅析
  8. 解决Viewpager满屏不能自适应填充内容的
  9. Android(安卓)SQLite数据库版本升级原理
  10. android 监听EditText输入字符长度