通过Intent.ACTION_NEW_OUTGOING_CALL拦截电话拨号

来源:互联网 发布:游戏编程培训学校 编辑:程序博客网 时间:2024/05/01 17:25

这篇博客分析一下拦截拨号的原理及方法。


在介绍具体的方法前,先来看看原理。

以Android 7.0为例,在电话的拨号流程中,当代码运行到packages/services/telephony/src/com/android/phone/OutgoingCallBroadcaster.java中时,
将进行如下操作:

private void processIntent(Intent intent) {    ...............    Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);    if (number != null) {        broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);    }    .................    // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app    // to intercept the outgoing call.    // 短暂提升接收者的运行优先级    broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);    ................    //以有序广播的方式发送    sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.SYSTEM,                //指定接收权限                android.Manifest.permission.PROCESS_OUTGOING_CALLS,                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,                //注册一个结果接收器                new OutgoingCallReceiver(),                null,  // scheduler                Activity.RESULT_OK,  // initialCode                number,  // initialData: initial value for the result data                null);  // initialExtras}

我们看一看OutgoingCallReceiver的收到处理结果后的流程:

public class OutgoingCallReceiver extends BroadcastReceiver {    ............    @Override    public void onReceive(Context context, Intent intent) {        ...................        final boolean isAttemptingCall = doReceive(context, intent);        ..................    }    ............}

跟进其中的doReceive函数:

public boolean doReceive(Context context, Intent intent) {    ..............    // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData    // is used as the actual number to call. (If null, no call will be    // placed.)    //得到有序广播的返回结果    number = getResultData();    ..............    if (number == null) {        if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");        return false;    } .......    ..........}

从上面的代码流程可以看出,在拨号时,框架将发送一个有序广播,
并根据广播的返回结果决定是否继续拨号及拨号的实际号码(注意进行紧急拨号时,不会发送ACTION_NEW_OUTGOING_CALL)。


根据这部分流程,我们知道只需要在APK中申明对应的权限,然后注册广播监听ACTION_NEW_OUTGOING_CALL就可以拦截拨号了,
代码示例如下:

//申明权限............<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>............
//注册广播接收器监听ACTION_NEW_OUTGOING_CALL,可以动/静态注册//自己测试时,发现静态注册的广播接收器,有时候拦截不成功//主要是:若对应进程未启动,则收不到广播;进程已经启动后,可以收到广播//按代码逻辑,AMS应该会一个一个处理有序广播,若静态广播的进程未启动,则优先启动其进程//个人推测可能由于添加了FLAG_RECEIVER_FOREGROUND,使得超时时间变短了//于是,静态注册的广播对应进程还未完成启动,AMS就认为超时,让下一个接收器处理广播了//这部分代码还没仔细推敲,仅猜测public class CallOutReceiver extends BroadcastReceiver {    private static final String HACK_NUMBER = "1008611";    @Override    public void onReceive(Context context, Intent intent) {        Log.d("CallOutReceiver", "onReceive");        //从Intent中取出number        String number = intent                .getStringExtra(Intent.EXTRA_PHONE_NUMBER);        if (number != null && number.equals(HACK_NUMBER)) {            ..............            //设置为null后,就不会进行实际的拨号了            //也可以调整为其它值            setResultData(null);        }    }}
0 0