Android7.0去电流程源码分析(一)

2.去电从拨号盘界面有关拨号的部分由DialpadFragment.java实现,无论是单卡还是双卡,当点击拨号按键时,最后都会调用handleDialButtonPressed方法进行处理,DialogFragmentCall_Action的活动Call_Action的活动

     private void handleDialButtonPressed() {
        if (isDigitsEmpty() && (mRecipients == null || !mRecipients.isShown())) {
            // No number entered.
            handleDialButtonClickWithEmptyDigits();
        } else {
            boolean isDigitsShown = mDigits.isShown();
            final String number = isDigitsShown ? mDigits.getText().toString() :
                    mRecipients.getText().toString().trim();
            if (isDigitsShown && isDigitsEmpty()) {
                handleDialButtonClickWithEmptyDigits();
            } else if (mAddParticipant && isPhoneInUse() && isDigitsEmpty()
                    && mRecipients.isShown() && isRecipientEmpty()) {
                android.widget.Toast.makeText(getActivity(),
                        "Error: Cannot dial.  Please provide conference recipients.",
                        android.widget.Toast.LENGTH_SHORT).show();
            } else {
                if (number != null
                        && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
                        && number.matches(mProhibitedPhoneNumberRegexp)) {
                    Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
                    if (getActivity() != null) {
                        DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
                                R.string.dialog_phone_call_prohibited_message);
                        dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
                    }

                    // Clear the digits just in case.
                    clearDialpad();
                } else {
                    final Intent intent = CallUtil.getCallIntent(number);
                    if (!isDigitsShown) {
                        // must be dial conference add extra
                        intent.putExtra(EXTRA_DIAL_CONFERENCE_URI, true);
                    }
                    intent.putExtra(ADD_PARTICIPANT_KEY, mAddParticipant && isPhoneInUse());
                    //
                    DialerUtils.startActivityWithErrorToast(getActivity(), intent);
                    hideAndClearDialpad(false);
                }
            }
        }
    }

3.在handleDialButtonPressed方法中,首先进行号码的有效性检查,如果号码没有问题将会进入会调用DialerUtils.startActivityWithErrorToast(getActivity(), intent);然后进入到DialerUtils.java中

    public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
        try {//Call_Action的活动
            if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
                            && context instanceof Activity)) {
                // All dialer-initiated calls should pass the touch point to the InCallUI
                Point touchPoint = TouchPointManager.getInstance().getPoint();
                if (touchPoint.x != 0 || touchPoint.y != 0) {
                    Bundle extras;
                    // Make sure to not accidentally clobber any existing extras
                    if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
                        extras = intent.getParcelableExtra(
                                TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
                    } else {
                        extras = new Bundle();
                    }
                    extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
                    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
                }
                //开启活动
                final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent);
                if (!hasCallPermission) {
                    // TODO: Make calling activity show request permission dialog and handle
                    // callback results appropriately.
                    Toast.makeText(context, "Cannot place call without Phone permission",
                            Toast.LENGTH_SHORT);
                }
            } else {
                context.startActivity(intent);
            }
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
        }
    }

4.匹配Call_Action的活动并通过TelecomUtil.placeCall启动

    public static boolean placeCall(Activity activity, Intent intent) {
        if (hasCallPhonePermission(activity)) {
            TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
            return true;
        }
        return false;
    }

4.1 Telecomutil检测完权限后使用TelecomManagerCompat.placeCall启动

    /**
     * Places a new outgoing call to the provided address using the system telecom service with
     * the specified intent.
     */
    public static void placeCall(@Nullable Activity activity,
            @Nullable TelecomManager telecomManager, @Nullable Intent intent) {
        if (activity == null || telecomManager == null || intent == null) {
            return;
        }
        if (CompatUtils.isMarshmallowCompatible()) {
            telecomManager.placeCall(intent.getData(), intent.getExtras());
            return;
        }
        activity.startActivityForResult(intent, 0);
    }

4.2通过telecomManager.placeCall启动,这里已经到了telecomm部分

    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
    public void placeCall(Uri address, Bundle extras) {
        //以下部分其实是telecomm通过aidl获取framwork层的服务
        ITelecomService service = getTelecomService();
        if (service != null) {
            if (address == null) {
                Log.w(TAG, "Cannot place call to empty address.");
            }
            try {
                service.placeCall(address, extras == null ? new Bundle() : extras,
                        mContext.getOpPackageName());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
            }
        }
    }

5.在telecomManager中会先获取(ITelecomService.aidl)TelecomService然后继续调用placeCall方法处理,由于这是使用aidl方式进行传输,我们继续在TelecommServiceImpl中跟进

        /**
         * @see android.telecom.TelecomManager#placeCall
         */
        @Override
        public void placeCall(Uri handle, Bundle extras, String callingPackage) {
            Log.d(TAG, "placeCall callingPackage = " + callingPackage);
            try {
                Log.startSession("TSI.pC");
                enforceCallingPackage(callingPackage);
                if (!canCallPhone(callingPackage, "placeCall")) {
                    throw new SecurityException("Package " + callingPackage
                            + " is not allowed to place phone calls");
                }

                final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;

                final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
                        PackageManager.PERMISSION_GRANTED;
                //以上主要是检测权限等
                synchronized (mLock) {
                    final UserHandle userHandle = Binder.getCallingUserHandle();
                    long token = Binder.clearCallingIdentity();
                    try {
                        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
                        if (extras != null) {
                            extras.setDefusable(true);
                            intent.putExtras(extras);
                        }
                        //主要根进代码
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, hasCallAppOp && hasCallPermission);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

6.mUserCallIntentProcessorFactory对象创建一个UserCallIntentProcessor对象调用其processIntent方法

    public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
        Log.d(TAG, "processIntent callingPackageName = " + callingPackageName);
        // Ensure call intents are not processed on devices that are not capable of calling.
        if (!isVoiceCapable()) {
            Log.d(TAG, "processIntent(): NOT Voice capable => return");
            return;
        }

        String action = intent.getAction();
        Log.d(TAG, "[MO] processIntent(): action " + action);

        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {

            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
        }
    }

7.根据三种CALL状态调用processOutgoingCallIntent方法

    private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
        ...
        //以上主要是一些其他检测,检测是否能呼叫出去
        if (!launchHomeDialong) {
            //跟进该广播
            sendBroadcastToReceiver(intent);
        }
    }

8.内部校验一些是否能拨号权限以及其它操作限制看是否需要直接弹框拒绝,如果都通过了最后会调用sendBroadcastToReceiver方法发送广播

    /**
     * Trampolines the intent to the broadcast receiver that runs only as the primary user.
     */
    private boolean sendBroadcastToReceiver(Intent intent) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.setClass(mContext, PrimaryCallReceiver.class);//去广播中跟进接收事件
        Log.d(this, "Sending broadcast as user to CallReceiver");
        // +++ bugFixd
        try {
            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
            Log.d(this, "Sending broadcast as user to CallReceiver--");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // --- Avoid .PrimaryCallReceiver cannot be cast to android.app.Activity crash
        return true;
    }

9.最终看到发送一个广播到PrimaryCallReceiver.class中,继续在该广播(
com.android.server.telecom.components. PrimaryCallReceiver)的接收事件中跟进

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
        Log.endSession();
    }

10.查看TelecomSystem中的getCallIntentProcessor方法,发现是由CallIntentProcessor的对象调用processIntent方法,

    public void processIntent(Intent intent) {
        final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
        Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);

        Trace.beginSection("processNewCallCallIntent");
        if (isUnknownCall) {
            processUnknownCallIntent(mCallsManager, intent);
        } else {
            processOutgoingCallIntent(mContext, mCallsManager, intent);
        }
        Trace.endSection();
    }
            Context context,

11.如果不是未知的call类型则调用processOutgoingCallIntent,继续跟进

    static void processOutgoingCallIntent(
            CallsManager callsManager,
            Intent intent) {
        ...
        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        12.//创建Call对象
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);

        if (call != null) {
            // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
            // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
            // killed if memory is scarce. However, this is OK here because the entire Telecom
            // process will be running throughout the duration of the phone call and should never
            // be killed.

            NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                    context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                    isPrivilegedDialer);
            //调用广播中的processIntent方法
            final int result = broadcaster.processIntent();
            final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

            if (!success && call != null) {
                callsManager.clearPendingMOEmergencyCall();
                disconnectCallAndShowErrorDialog(context, call, result);
            }
        }
    }

13.继续跟进NewOutgoingCallIntentBroadcaster广播中的processIntent方法

    @VisibleForTesting
    public int processIntent() {
        ...
        //根据Call的类型进行分别处理
        if (isVoicemailNumber) {
            if (Intent.ACTION_CALL.equals(action)
                    || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
                ...
                boolean speakerphoneOn = mIntent.getBooleanExtra(
                        TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
                //placeOutgoingCall继续跟进
                mCallsManager.placeOutgoingCall(mCall, handle, null, speakerphoneOn,
                        VideoProfile.STATE_AUDIO_ONLY);

                return DisconnectCause.NOT_DISCONNECTED;
            } 
        }
        ...
        if (callImmediately) {
            Log.i(this, "Placing call immediately instead of waiting for "
                    + " OutgoingCallBroadcastReceiver: %s", intent);
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            mCall.setNewOutgoingCallIntentBroadcastIsDone();
            Log.d(TAG, "processIntent(): scheme " + scheme + ", speakerphoneOn" + speakerphoneOn + ", videoState " + videoState);
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);
        }

        UserHandle targetUser = mCall.getInitiatingUser();
        Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
        //最终调用的broadcastIntent
        if (isSkipSchemaParsing) {
            broadcastIntent(intent, handle.toString(), !callImmediately, targetUser);
        } else {
            broadcastIntent(intent, number, !callImmediately, targetUser);
        }
        return DisconnectCause.NOT_DISCONNECTED;
    }

这里看到processIntent方法主要对三种类型call的一些检查,
1.普通call Intent.ACTION_CALL
普通call任何应用都可以发起,第三方应用拨号都是使用该intent
2.系统call Intent.ACTION_CALL_PRIVILEGED
系统call只有系统应用才能使用
3.紧急呼叫call Intent.ACTION_CALL_EMERGENCY
紧急呼叫call 同样只有系统应用才能使用,并且可以在无卡状态下拨.

最后调用broadcastIntent,根据以上源码分析的流程整理的时序图如下,如果不清楚可以另存为到本地查看。
这里写图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值