首页 > 代码库 > 高版本Android如何利用反射调用系统隐藏的远程服务拦截来电
高版本Android如何利用反射调用系统隐藏的远程服务拦截来电
要说拦截Android系统来电,就不得不说起在低版本的时候Android提供给开发者使用的一个方法:endCall(),但由于谷歌后来考虑到对于一部手机来说,最重要的功能就是打电话了,如果这个功能随随便便就被人屏蔽了,安全性太差,所以在高版本的Android将这个方法屏蔽了,不再在TelephoneManager中暴露这个方法。
那么我们下面的目标就是要想办法调用到这个方法,当然首先我们还是需要实现一个广播接收者,来接收电话状态改变的广播,这里使用在服务中动态注册广播接收者的方法来实现,主要好处在于便于控制广播接收者的生命周期,同时也能比静态注册有更高的权限
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; @Override public IBinder onBind(Intent intent) { return null; } @Override public 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; @Override public 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("挂断电话"); <strong>hangUpCallFromBlacklist(incomingNumber);//挂断电话的方法 } } break; default: break; } } }; registerReceiver(callReceiver, filter);// 注册广播接收者 }
在服务的onDestroy()方法中取消注册广播接收者:
@Override public 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; @Override public 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入手:
我们可以发现这个方法是定义在ContextWrapper中的,而ContextWrapper又是继承自Context,我们不妨进入到Context的源码中去一探究竟:
在Context中,我们只找到了一个getSystemService的抽象方法,那么如何去找实现方法呢,在java中,抽象类的实现类一般名字都是在抽象类名后面加上一个"Impl",于是我们去搜索源码中的ContextImpl.java,找到之后打开发现:
我们发现getSystemService实际上返回了一个ServiceFetcher对象的一个getService方法的结果,我们来看看ServiceFetcher的getService方法:
在这里我们可以看到,在定义了一个static块中,注册了很多不同的service服务,而这些gerService方法都是由ServiceManager来调用的,返回值是一个IBinder对象。接下来我们可以在文件的导入包的部分找到ServiceManager的位置是在android/os下的:
由于getService返回的是一个IBinder对象,我们只要找到这个getService方法的实现,就可以传入TELEPONY_SERVICE从而拿到真正的TelephonyManager所代理的那个远程服务绑定对象,从而调用隐藏在其中的endCall方法。
幸运的是,在ServiceMnager.java中,我们找到了getService方法的实现:
我们可以发现,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的源码中去看看:
我们发现,在TelephonyManager中,类似于getCallState()这类的方法基本都返回的是getITelephony()的返回值调用的方法,那么这个getITelephy()是什么呢:
我们发现,返回的实际上是一个ITelephony对象,而且是以一种调用远程服务方法的形式返回的;
我们在文件的头部找到ITelephony的位置:
打开上面的目录,我们发现ITelephony是一个aidl文件,进入其中,我们可以找到endCall方法:
由于在ITelephony.aidl的头部有如下信息:
我们想要通过aidl来调用远程服务telephony的方法endCall(),我们需要将Telehpony.aidl拷贝到我们工程中新建的com.android.internal.telephony包中,同时将android.telephony.NeighboringCellInfo.aidl文件拷贝到工程中新建的android.telephony包中,这样在gen目中下就会自动生成一个对应的ITelephony.java文件。至此,我们就可以使用下面的语句来调用远程服务的endCall方法:
ITelephony.Stub.asInterface(binder).endCall();
最后,不要忘记在清单文件中加入对应的权限:
<!-- 授予该应用控制通话的权限 --> <uses-permission android:name="android.permission.CALL_PHONE"> <!-- 授予该应用读取通话状态的权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE">
高版本Android如何利用反射调用系统隐藏的远程服务拦截来电