要说拦截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;
@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入手:


android拦截方法 安卓系统拦截设置方法_aidl

android拦截方法 安卓系统拦截设置方法_android拦截方法_02


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


android拦截方法 安卓系统拦截设置方法_android拦截方法_03


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


android拦截方法 安卓系统拦截设置方法_android_04


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


android拦截方法 安卓系统拦截设置方法_ITelephony_05



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


android拦截方法 安卓系统拦截设置方法_aidl_06


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

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


android拦截方法 安卓系统拦截设置方法_远程服务_07

android拦截方法 安卓系统拦截设置方法_android_08


我们可以发现,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拦截方法 安卓系统拦截设置方法_android_09


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


android拦截方法 安卓系统拦截设置方法_aidl_10


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

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


android拦截方法 安卓系统拦截设置方法_android_11


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


android拦截方法 安卓系统拦截设置方法_远程服务_12


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


android拦截方法 安卓系统拦截设置方法_aidl_13


我们想要通过aidl来调用远程服务ITelephony的方法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">