Android 通话流程相关分析

Telephony上层的代码主要由六部分组成:

frameworks/opt/telephony/    telephony-common.jar
frameworks/base/telecomm/    framework-telecomm    framework.jar
frameworks/base/telephony/    framework-telephony    framework.jar
packages/services/Telephony/    TeleService.apk
packages/services/Telecomm/    Telecom.apk
packages/apps/Dialer/    Dialer.apk

本文中代码来源于MTK Android 14源码。

MO 显示 InCall UI 流程

显示 InCall UI 流程主要分为 3 部分:
1.通过CallIntentProcessor处理Call Intent ;
2.InCallController 绑定 InCallService;
3.InCallService 通过 InCallPresenter 拉起UI;

1.拨出电话从 DialpadFragment 的拨号按钮按下开始,直到调用到 TelecomManager 的 placeCall()方法进入到真正的流程入口

packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadFragment.java

public void onClick(View view) {
        int resId = view.getId();
        if (resId == R.id.dialpad_floating_action_button) {
            view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
            handleDialButtonPressed();
        } else if (resId == R.id.deleteButton) {
        ...
        }
    }

    private void handleDialButtonPressed() {
        if (isDigitsEmpty()) { // No number entered.
            // No real call made, so treat it as a click
            PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
            handleDialButtonClickWithEmptyDigits();
        } else {
            final String number = digits.getText().toString();
            
            ...
        } else{
            PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD));
            hideAndClearDialpad();
        }
    }

packages/apps/Dialer/java/com/android/dialer/precall/PreCall.java

    static void start(Context context, CallIntentBuilder builder) {
        DialerUtils.startActivityWithErrorToast(context, getIntent(context, builder));
    }

packages/apps/Dialer/java/com/android/dialer/util/DialerUtils.java

    public static void startActivityWithErrorToast(
            final Context context, final Intent intent, int msgId) {
        try {
            if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
                // All dialer-initiated calls should pass the touch point to the InCallUI
                Point touchPoint = TouchPointManager.getInstance().getPoint();
                ...
                if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
                    ...
                } else {
                    placeCallOrMakeToast(context, intent);
                }
            } else {
                context.startActivity(intent);
            }
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
        }
    }

    private static void placeCallOrMakeToast(Context context, Intent intent) {
        final boolean hasCallPermission = TelecomUtil.placeCall(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)
                    .show();
        }
    }

2.TelecomManager 的服务实现 TelecomServiceImpl 将检查发起拨号的应用是否有拨号的权限,如果有则开始处理 拨号的 Intent

packages/apps/Dialer/java/com/android/dialer/telecom/TelecomUtil.java

    public static boolean placeCall(Context context, Intent intent) {
        if (hasCallPhonePermission(context)) {
            //第一次跨进程调用
            getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
            return true;
        }
        return false;
    }

frameworks/base/telecomm/java/android/telecom/TelecomManager.java

    public void placeCall(Uri address, Bundle extras) {
        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(), mContext.getAttributionTag());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
            }
        }
    }

3.拨号 Intent 由 UserCallIntentProcessor 处理,最终调用到 CallIntentProcessor 进行进一步处理

packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

    public void placeCall(Uri handle, Bundle extras, String callingPackage,
                          String callingFeatureId) {
        try {
            Log.startSession("TSI.pC");
            enforceCallingPackage(callingPackage);

            ...

            synchronized (mLock) {
                final UserHandle userHandle = Binder.getCallingUserHandle();
                long token = Binder.clearCallingIdentity();
                try {
                    final Intent intent = new Intent(hasCallPrivilegedPermission ?
                            Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                    if (extras != null) {
                        extras.setDefusable(true);
                        intent.putExtras(extras);
                    }
                    mUserCallIntentProcessorFactory.create(mContext, userHandle)
                            .processIntent(
                                    intent, callingPackage, isSelfManaged ||
                                            (hasCallAppOp && hasCallPermission),
                                    true /* isLocalInvocation */);
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
            }
        } finally {
            Log.endSession();
        }
    }

packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java

    public void processIntent(Intent intent, String callingPackageName,
                              boolean canCallNonEmergency, boolean isLocalInvocation) {
        // Ensure call intents are not processed on devices that are not capable of calling.
        if (!isVoiceCapable()) {
            return;
        }

        String action = intent.getAction();

        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
                    isLocalInvocation);
        }
    }

    private void processOutgoingCallIntent(Intent intent, String callingPackageName,
                                           boolean canCallNonEmergency, boolean isLocalInvocation) {
        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        ...

        if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
            showErrorDialogForRestrictedOutgoingCall(mContext,
                    R.string.outgoing_call_not_allowed_no_permission);
            Log.w(this, "Rejecting non-emergency phone call because "
                    + android.Manifest.permission.CALL_PHONE + " permission is not granted.");
            return;
        }

        int videoState = intent.getIntExtra(
                TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        Log.d(this, "processOutgoingCallIntent videoState = " + videoState);

        // Save the user handle of current user before forwarding the intent to primary user.
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
    }

    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,
                                            String callingPackage) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        if (isLocalInvocation) {
            // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
            // bother trampolining the intent, just sent it directly to the call intent processor.
            // TODO: We should not be using an intent here; this whole flows needs cleanup.
            Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
            synchronized (TelecomSystem.getInstance().getLock()) {
                TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,
                        callingPackage);
            }
        } else {
            // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
            // process; we need to trampoline to TelecomSystem in the system server process.
            Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
            tm.handleCallIntent(intent, callingPackage);
        }
        return true;
    }

4.CallIntentProcessor 有两个流程分支,其中拉起 InCall UI 是直接进入 CallsManager 中调用 addCall 方法,并唤醒监听该消息的 listener

packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

    public void processIntent(Intent intent, String callingPackage) {
        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, callingPackage,
                    mDefaultDialerCache);
        }
        Trace.endSection();
    }

    static void processOutgoingCallIntent(
            Context context,
            CallsManager callsManager,
            Intent intent,
            String callingPackage,
            DefaultDialerCache defaultDialerCache) {

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        ...

        boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
                initiatingUser.getIdentifier());

        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer, defaultDialerCache);

        // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
        NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
        if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
            showErrorDialog(context, disposition.disconnectCause);
            return;
        }

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        CompletableFuture<Call> callFuture = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent, callingPackage);

        final Session logSubsession = Log.createSubsession();
        callFuture.thenAccept((call) -> {
            if (call != null) {
                Log.continueSession(logSubsession, "CIP.sNOCI");
                try {
                    broadcaster.processCall(call, disposition);
                } finally {
                    Log.endSession();
                }
            }
        });
    }

packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

    private CompletableFuture<Call> startOutgoingCall(List<Uri> participants,
                                                      PhoneAccountHandle requestedAccountHandle,
                                                      Bundle extras, UserHandle initiatingUser, Intent originalIntent,
                                                      String callingPackage, boolean isConference) {
        boolean isReusedCall;
        Uri handle = isConference ? Uri.parse("tel:conf-factory") : participants.get(0);
        Call call = reuseOutgoingCall(handle);

        PhoneAccount account =
                mPhoneAccountRegistrar.getPhoneAccount(requestedAccountHandle, initiatingUser);
        Bundle phoneAccountExtra = account != null ? account.getExtras() : null;
        boolean isSelfManaged = account != null && account.isSelfManaged();

        ...

        mLatestPostSelectionProcessingFuture = dialerSelectPhoneAccountFuture
                .thenComposeAsync(args -> {
                    if (args == null) {
                        return CompletableFuture.completedFuture(null);
                    }
                    Log.i(CallsManager.this, "post acct selection stage");
                    Call callToUse = args.first;
                    PhoneAccountHandle phoneAccountHandle = args.second;
                    PhoneAccount accountToUse = mPhoneAccountRegistrar
                            .getPhoneAccount(phoneAccountHandle, initiatingUser);
                    callToUse.setTargetPhoneAccount(phoneAccountHandle);
                    if (accountToUse != null && accountToUse.getExtras() != null) {
                        if (accountToUse.getExtras()
                                .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
                            Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
                                    callToUse.getId());
                            callToUse.setIsVoipAudioMode(true);
                        }
                    }

                    callToUse.setState(
                            CallState.CONNECTING,
                            phoneAccountHandle == null ? "no-handle"
                                    : phoneAccountHandle.toString());

                    boolean isVoicemail = isVoicemail(callToUse.getHandle(), accountToUse);

                    boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
                    if (!isVoicemail && (isRttSettingOn || (extras != null
                            && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT,
                            false)))) {
                        Log.d(this, "Outgoing call requesting RTT, rtt setting is %b",
                                isRttSettingOn);
                        if (callToUse.isEmergencyCall() || (accountToUse != null
                                && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT))) {
                            // If the call requested RTT and it's an emergency call, ignore the
                            // capability and hope that the modem will deal with it somehow.
                            callToUse.createRttStreams();
                        }
                        // Even if the phone account doesn't support RTT yet,
                        // the connection manager might change that. Set this to check it later.
                        callToUse.setRequestedToStartWithRtt();
                    }

                    setIntentExtrasAndStartTime(callToUse, extras);
                    setCallSourceToAnalytics(callToUse, originalIntent);

                    if (isPotentialMMICode(handle) && !isSelfManaged) {
                        // Do not add the call if it is a potential MMI code.
                        callToUse.addListener(this);
                    } else if (!mCalls.contains(callToUse)) {
                        // We check if mCalls already contains the call because we could
                        // potentially be reusing
                        // a call which was previously added (See {@link #reuseOutgoingCall}).
                        addCall(callToUse);
                    }
                    return CompletableFuture.completedFuture(callToUse);
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP", mLock));
        return mLatestPostSelectionProcessingFuture;
    }

    public void addCall(Call call) {
        Trace.beginSection("addCall");
        Log.i(this, "addCall(%s)", call);
        call.addListener(this);
        mCalls.add(call);

        // Specifies the time telecom finished routing the call. This is used by the dialer for
        // analytics.
        Bundle extras = call.getIntentExtras();
        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
                SystemClock.elapsedRealtime());

        updateCanAddCall();
        updateHasActiveRttCall();
        updateExternalCallCanPullSupport();
        // 快速设置call对象为前端通话,即第一通电话
        for (CallsManagerListener listener : mListeners) {
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.beginSection(listener.getClass().toString() + " addCall");
            }
            //通知InCallController调用onCallAdded
            listener.onCallAdded(call);
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.endSection();
            }
        }
        Trace.endSection();
    }

5.InCallController 监听到 onCallAdded()消息,framework_telecom属于framework,需要绑定InCallService才能元撑调用,开始绑定 InCallService,最终流程进入到InCallServiceImpl 子类实现中

packages/services/Telecomm/src/com/android/server/telecom/InCallController.java

    public void onCallAdded(Call call) {
        if (!isBoundAndConnectedToServices()) {    //判断是否已绑定
            Log.i(this, "onCallAdded: %s; not bound or connected.", call);
            // We are not bound, or we're not connected.
            bindToServices(call);
        } else {
            // We are bound, and we are connected.
            adjustServiceBindingsForEmergency();

            // This is in case an emergency call is added while there is an existing call.
            mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
                    mCallsManager.getCurrentUserHandle());

            Log.i(this, "onCallAdded: %s", call);
            // Track the call if we don't already know about it.
            addCall(call);

            Log.i(this, "mInCallServiceConnection isConnected=%b",
                    mInCallServiceConnection.isConnected());

            List<ComponentName> componentsUpdated = new ArrayList<>();
            for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
                InCallServiceInfo info = entry.getKey();

                if (call.isExternalCall() && !info.isExternalCallsSupported()) {
                    continue;
                }

                if (call.isSelfManaged() && (!call.visibleToInCallService()
                        || !info.isSelfManagedCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = false;
                if (mInCallServiceConnection != null) {
                    includeRttCall = info.equals(mInCallServiceConnection.getInfo());
                }

                componentsUpdated.add(info.getComponentName());
                IInCallService inCallService = entry.getValue();

                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                        true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(), includeRttCall,
                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
                                info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
                try {
                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                    updateCallTracking(call, info, true /* isAdd */);
                } catch (RemoteException ignored) {
                }
            }
            Log.i(this, "Call added to components: %s", componentsUpdated);
        }
    }

    public void bindToServices(Call call) {
        if (mInCallServiceConnection == null) {
            InCallServiceConnection dialerInCall = null;
            InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
            Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
            if (defaultDialerComponentInfo != null &&
                    !defaultDialerComponentInfo.getComponentName().equals(
                            mDefaultDialerCache.getSystemDialerComponent())) {
                dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
            }
            Log.i(this, "defaultDialer: " + dialerInCall);

            InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
                    mDefaultDialerCache.getSystemDialerComponent(), IN_CALL_SERVICE_TYPE_SYSTEM_UI);
            EmergencyInCallServiceConnection systemInCall =
                    new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
            systemInCall.setHasEmergency(mCallsManager.isInEmergencyCall());

            InCallServiceConnection carModeInCall = null;
            InCallServiceInfo carModeComponentInfo = getCurrentCarModeComponent();
            if (carModeComponentInfo != null &&
                    !carModeComponentInfo.getComponentName().equals(
                            mDefaultDialerCache.getSystemDialerComponent())) {
                carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
            }

            mInCallServiceConnection =
                    new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
        }

        mInCallServiceConnection.chooseInitialInCallService(shouldUseCarModeUI());

        // 在这里开始绑定InCallService.
        if (mInCallServiceConnection.connect(call) ==
                InCallServiceConnection.CONNECTION_SUCCEEDED || call.isSelfManaged()) {
            // Only connect to the non-ui InCallServices if we actually connected to the main UI
            // one, or if the call is self-managed (in which case we'd still want to keep Wear, BT,
            // etc. informed.
            connectToNonUiInCallServices(call);
            mBindingFuture = new CompletableFuture<Boolean>().completeOnTimeout(false,
                    mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
                            mContext.getContentResolver()),
                    TimeUnit.MILLISECONDS);
        } else {
            Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
        }

        IntentFilter packageChangedFilter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
        packageChangedFilter.addDataScheme("package");
        mContext.registerReceiver(mPackageChangedReceiver, packageChangedFilter);
    }

    private void connectToNonUiInCallServices(Call call) {
        if (mNonUIInCallServiceConnections == null) {
            updateNonUiInCallServices();
        }
        mNonUIInCallServiceConnections.connect(call);
    }

    private class NonUIInCallServiceConnectionCollection extends InCallServiceConnection {

        @Override
        public int connect(Call call) {
            for (InCallServiceBindingConnection subConnection : mSubConnections) {
                subConnection.connect(call);
            }
            return CONNECTION_SUCCEEDED;
        }
}

mNonUIInCallServiceConnections是NonUIInCallServiceConnectionCollection实例。实际调用InCallServiceBindingConnection的connect()方法。InCallServiceBindingConnection是的内部类。

private class InCallServiceBindingConnection extends InCallServiceConnection {
    
    private final ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.startSession("ICSBC.oSC", Log.getPackageAbbreviation(name));
                synchronized (mLock) {
                    try {
                        Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                        mIsBound = true;
                        if (mIsConnected) {
                            // Only proceed if we are supposed to be connected.
                            onConnected(service);
                        }
                    } finally {
                        Log.endSession();
                    }
                }
            }
    }

    public int connect(Call call) {
        if (mIsConnected) {
            Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request: "
                    + mInCallServiceInfo);
            if (call != null) {
                // Track the call if we don't already know about it.
                addCall(call);

                // Notify this new added call
                sendCallToService(call, mInCallServiceInfo,
                        mInCallServices.get(mInCallServiceInfo));
            }
            return CONNECTION_SUCCEEDED;
        }

        if (call != null && call.isSelfManaged() &&
                (!mInCallServiceInfo.isSelfManagedCallsSupported()
                        || !call.visibleToInCallService())) {
            Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
                    mInCallServiceInfo);
            mIsConnected = false;
            return CONNECTION_NOT_SUPPORTED;
        }

        Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
        intent.setComponent(mInCallServiceInfo.getComponentName());
        if (call != null && !call.isIncoming() && !call.isExternalCall()) {
            intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
                    call.getIntentExtras());
            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                    call.getTargetPhoneAccount());
        }

        Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
        mIsConnected = true;
        mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
        if (!mContext.bindServiceAsUser(intent, mServiceConnection,
                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
                UserHandle.CURRENT)) {
            Log.w(this, "Failed to connect.");
            mIsConnected = false;
        }

        if (mIsConnected && call != null) {
            mCall = call;
        }
        Log.i(this, "mCall: %s, mIsConnected: %s", mCall, mIsConnected);

        return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
    }
}

ServiceConnection绑定成功以后调用InCallServiceBindingConnection.mServiceConnection .onServiceConnected(),onServiceConnected()方法内调用了InCallServiceBindingConnection.onConnected()。

packages/services/Telecomm/src/com/android/server/telecom/InCallController.java

private class InCallServiceBindingConnection extends InCallServiceConnection {
        protected void onConnected(IBinder service) {
            boolean shouldRemainConnected =
                    InCallController.this.onConnected(mInCallServiceInfo, service);
            if (!shouldRemainConnected) {
                // Sometimes we can opt to disconnect for certain reasons, like if the
                // InCallService rejected our initialization step, or the calls went away
                // in the time it took us to bind to the InCallService. In such cases, we go
                // ahead and disconnect ourselves.
                disconnect();
            }
        }

}

    private boolean onConnected(InCallServiceInfo info, IBinder service) {
        Log.i(this, "onConnected to %s", info.getComponentName());

        if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
                || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
                || info.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI) {
            trackCallingUserInterfaceStarted(info);
        }
        IInCallService inCallService = IInCallService.Stub.asInterface(service);
        mInCallServices.put(info, inCallService);

        try {
            inCallService.setInCallAdapter(
                    new InCallAdapter(
                            mCallsManager,
                            mCallIdMapper,
                            mLock,
                            info.getComponentName().getPackageName()));
        } catch (RemoteException e) {
            Log.e(this, e, "Failed to set the in-call adapter.");
            Trace.endSection();
            return false;
        }

        // Upon successful connection, send the state of the world to the service.
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        for (Call call : calls) {
            //
            numCallsSent += sendCallToService(call, info, inCallService);
        }
        try {
            inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
            inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
        } catch (RemoteException ignored) {
        }
        // Don't complete the binding future for non-ui incalls
        if (info.getType() != IN_CALL_SERVICE_TYPE_NON_UI && !mBindingFuture.isDone()) {
            mBindingFuture.complete(true);
        }

        Log.i(this, "%s calls sent to InCallService.", numCallsSent);
        return true;
    }

    private int sendCallToService(Call call, InCallServiceInfo info,
            IInCallService inCallService) {
        try {
            if ((call.isSelfManaged() && (!info.isSelfManagedCallsSupported()
                    || !call.visibleToInCallService())) ||
                    (call.isExternalCall() && !info.isExternalCallsSupported())) {
                return 0;
            }

            // Only send the RTT call if it's a UI in-call service
            boolean includeRttCall = false;
            if (mInCallServiceConnection != null) {
                includeRttCall = info.equals(mInCallServiceConnection.getInfo());
            }

            // Track the call if we don't already know about it.
            addCall(call);
            ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
                    call,
                    true /* includeVideoProvider */,
                    mCallsManager.getPhoneAccountRegistrar(),
                    info.isExternalCallsSupported(),
                    includeRttCall,
                    info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
                            info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
            //
            inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
            updateCallTracking(call, info, true /* isAdd */);
            return 1;
        } catch (RemoteException ignored) {
        }
        return 0;
    }    

frameworks/base/telecomm/java/android/telecom/InCallService.java

    private final class InCallServiceBinder extends IInCallService.Stub {

        @Override
        public void addCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
        }
    }

    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
                return;
            }

            switch (msg.what) {
                case MSG_ADD_CALL:
                    mPhone.internalAddCall((ParcelableCall) msg.obj);
                    break;
            }
        }
    }

    private Phone.Listener mPhoneListener = new Phone.Listener() {

        /**
         * ${inheritDoc}
         */
        @Override
        public void onCallAdded(Phone phone, Call call) {
            InCallService.this.onCallAdded(call);
        }
    }

frameworks/base/telecomm/java/android/telecom/Phone.java

final void internalAddCall(ParcelableCall parcelableCall) {
        if (mTargetSdkVersion < SDK_VERSION_R
                && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
            Log.i(this, "Skipping adding audio processing call for sdk compatibility");
            return;
        }

        Call call = mCallByTelecomCallId.get(parcelableCall.getId());
        if (call == null) {
            call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                    parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
            mCallByTelecomCallId.put(parcelableCall.getId(), call);
            mCalls.add(call);
            checkCallTree(parcelableCall);
            call.internalUpdate(parcelableCall, mCallByTelecomCallId);
            fireCallAdded(call);
        } else {
            Log.w(this, "Call %s added, but it was already present", call.internalGetCallId());
            checkCallTree(parcelableCall);
            call.internalUpdate(parcelableCall, mCallByTelecomCallId);
        }
    }

    private void fireCallAdded(Call call) {
        for (Listener listener : mListeners) {
            listener.onCallAdded(this, call);    //回调匿名
        }
    }

6.InCallServiceImpl 调用 InCallPresenter 的 onCallAdded()方法

packages/apps/Dialer/java/com/android/incallui/InCallServiceImpl.java

    public void onCallAdded(Call call) {
        Trace.beginSection("InCallServiceImpl.onCallAdded");
        InCallPresenter.getInstance().onCallAdded(call);
        Trace.endSection();
    }

packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java

    public void onCallAdded(final android.telecom.Call call) {
        Trace.beginSection("InCallPresenter.onCallAdded");
        LatencyReport latencyReport = new LatencyReport(call);
        if (shouldAttemptBlocking(call)) {
            maybeBlockCall(call, latencyReport);
        } else {
            if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
                externalCallList.onCallAdded(call);
            } else {
                latencyReport.onCallBlockingDone();
                callList.onCallAdded(context, call, latencyReport);
            }
        }

        // Since a call has been added we are no longer waiting for Telecom to send us a call.
        setBoundAndWaitingForOutgoingCall(false, null);
        call.registerCallback(callCallback);
        // TODO(maxwelb): Return the future in recordPhoneLookupInfo and propagate.
        PhoneLookupHistoryRecorder.recordPhoneLookupInfo(context.getApplicationContext(), call);
        Trace.endSection();
    }

7.InCallPresenter 通知 CallList 有call 被添加,则创建出 DialerCall 对象用于存储 Dialer 中的Call状态

packages/apps/Dialer/java/com/android/incallui/call/CallList.java

    public void onCallAdded(
            final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
        Trace.beginSection("CallList.onCallAdded");
        if (telecomCall.getState() == Call.STATE_CONNECTING) {
            MetricsComponent.get(context)
                    .metrics()
                    .startTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
        } else if (telecomCall.getState() == Call.STATE_RINGING) {
            MetricsComponent.get(context)
                    .metrics()
                    .startTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
        }
        if (uiListeners != null) {
            uiListeners.onCallAdded();
        }
        final DialerCall call =
                new DialerCall(context, this, telecomCall, latencyReport, true /* registerCallback */);
        if (getFirstCall() != null) {
            logSecondIncomingCall(context, getFirstCall(), call);
        }

        EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
        manager.registerCapabilitiesListener(call);
        manager.registerStateChangedListener(call);

        Trace.beginSection("checkSpam");
        call.addListener(new DialerCallListenerImpl(call));
        LogUtil.d("CallList.onCallAdded", "callState=" + call.getState());
        if (SpamComponent.get(context).spamSettings().isSpamEnabled()) {
            String number = TelecomCallUtil.getNumber(telecomCall);
            ListenableFuture<SpamStatus> futureSpamStatus =
                    SpamComponent.get(context).spam().checkSpamStatus(number, call.getCountryIso());

            Futures.addCallback(
                    futureSpamStatus,
                    new FutureCallback<SpamStatus>() {
                        @Override
                        public void onSuccess(@Nullable SpamStatus result) {
                            boolean isIncomingCall =
                                    call.getState() == DialerCallState.INCOMING
                                            || call.getState() == DialerCallState.CALL_WAITING;
                            boolean isSpam = result.isSpam();
                            call.setSpamStatus(result);

                            if (isIncomingCall) {
                                Logger.get(context)
                                        .logCallImpression(
                                                isSpam
                                                        ? DialerImpression.Type.INCOMING_SPAM_CALL
                                                        : DialerImpression.Type.INCOMING_NON_SPAM_CALL,
                                                call.getUniqueCallId(),
                                                call.getTimeAddedMs());
                            }
                            onUpdateCall(call);
                            notifyGenericListeners();
                        }

                        @Override
                        public void onFailure(Throwable t) {
                            LogUtil.e("CallList.onFailure", "unable to query spam status", t);
                        }
                    },
                    DialerExecutorComponent.get(context).uiExecutor());

            Trace.beginSection("updateUserMarkedSpamStatus");
            Trace.endSection();
        }
        Trace.endSection();

        Trace.beginSection("checkBlock");
        FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler =
                new FilteredNumberAsyncQueryHandler(context);

        filteredNumberAsyncQueryHandler.isBlockedNumber(
                new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
                    @Override
                    public void onCheckComplete(Integer id) {
                        if (id != null && id != FilteredNumberAsyncQueryHandler.INVALID_ID) {
                            call.setBlockedStatus(true);
                            // No need to update UI since it's only used for logging.
                        }
                    }
                },
                call.getNumber(),
                call.getCountryIso());
        Trace.endSection();

        if (call.getState() == DialerCallState.INCOMING
                || call.getState() == DialerCallState.CALL_WAITING) {
            if (call.isActiveRttCall()) {
                if (!call.isPhoneAccountRttCapable()) {
                    RttPromotion.setEnabled(context);
                }
                Logger.get(context)
                        .logCallImpression(
                                DialerImpression.Type.INCOMING_RTT_CALL,
                                call.getUniqueCallId(),
                                call.getTimeAddedMs());
            }
            onIncoming(call);
        } else {
            if (call.isActiveRttCall()) {
                Logger.get(context)
                        .logCallImpression(
                                DialerImpression.Type.OUTGOING_RTT_CALL,
                                call.getUniqueCallId(),
                                call.getTimeAddedMs());
            }
            onUpdateCall(call);
            notifyGenericListeners();
        }

        if (call.getState() != DialerCallState.INCOMING) {
            // Only report outgoing calls
            ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
        }

        Trace.endSection();
    }

8.DialerCall 注册监听 telecomCall 的状态,并根据其变化更新自身,随后将变化反映到UI 上
9.InCall UI 的显示通过 InCallPresenter 的 showInCall()方法实现

    public void showInCall(boolean showDialpad, boolean newOutgoingCall) {
        LogUtil.i("InCallPresenter.showInCall", "Showing InCallActivity");
        context.startActivity(
                InCallActivity.getIntent(context, showDialpad, newOutgoingCall, false /* forFullScreen */));
    }

MO 拨号流程

1.最初的流程与 拉起 InCallUI类似,先处理 Call Inetnt, 然后CallIntentProcessor 的另一支流程会创建 NewOutgoingCallIntentBroadcaster

packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

    static void processOutgoingCallIntent(
            Context context,
            CallsManager callsManager,
            Intent intent,
            String callingPackage,
            DefaultDialerCache defaultDialerCache) {

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        ...

        boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
                initiatingUser.getIdentifier());

        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer, defaultDialerCache);

        // evaluateCall()评估是否是紧急电话或voicemail
        NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
        if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
            showErrorDialog(context, disposition.disconnectCause);
            return;
        }

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        CompletableFuture<Call> callFuture = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent, callingPackage);

        final Session logSubsession = Log.createSubsession();
        callFuture.thenAccept((call) -> {
            if (call != null) {
                Log.continueSession(logSubsession, "CIP.sNOCI");
                try {
                    broadcaster.processCall(call, disposition);
                } finally {
                    Log.endSession();
                }
            }
        });
    }

2.通过 NewOutgoingCallIntentBroadcaster 对象进行紧急电话的评估,然后进入NewOutgoingCallIntentBroadcaster 处理Call 的流程。

packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

    public void processCall(Call call, CallDisposition disposition) {
        mCall = call;
        if (disposition.callImmediately) {
            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);
            placeOutgoingCallImmediately(mCall, disposition.callingAddress, null,
                    speakerphoneOn, videoState);

            // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
            // so that third parties can still inspect (but not intercept) the outgoing call. When
            // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
            // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
        }

        boolean callRedirectionWithService = false;
        if (disposition.requestRedirection) {
            CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor(
                    mContext, mCallsManager, mCall, disposition.callingAddress,
                    mCallsManager.getPhoneAccountRegistrar(),
                    getGateWayInfoFromIntent(mIntent, mIntent.getData()),
                    mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                            false),
                    mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                            VideoProfile.STATE_AUDIO_ONLY));
            /**
             * If there is an available {@link android.telecom.CallRedirectionService}, use the
             * {@link CallRedirectionProcessor} to perform call redirection instead of using
             * broadcasting.
             */
            callRedirectionWithService = callRedirectionProcessor
                    .canMakeCallRedirectionWithService();
            if (callRedirectionWithService) {
                callRedirectionProcessor.performCallRedirection();
            }
        }

        if (disposition.sendBroadcast) {
            UserHandle targetUser = mCall.getInitiatingUser();
            Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
            broadcastIntent(mIntent, disposition.number,
                    !disposition.callImmediately && !callRedirectionWithService, targetUser);
        }
    }

    public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                Log.startSession("NOCBIR.oR");
                Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
                synchronized (mLock) {
                    Log.v(this, "onReceive: %s", 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.)
                    String resultNumber = getResultData();
                    Log.i(this, "Received new-outgoing-call-broadcast for %s with data %s", mCall,
                            Log.pii(resultNumber));

                    boolean endEarly = false;
                    long disconnectTimeout =
                            Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver());
                    if (resultNumber == null) {
                        Log.v(this, "Call cancelled (null number), returning...");
                        disconnectTimeout = getDisconnectTimeoutFromApp(
                                getResultExtras(false), disconnectTimeout);
                        endEarly = true;
                    } else if (isPotentialEmergencyNumber(resultNumber)) {
                        Log.w(this, "Cannot modify outgoing call to emergency number %s.",
                                resultNumber);
                        disconnectTimeout = 0;
                        endEarly = true;
                    }

                    if (endEarly) {
                        if (mCall != null) {
                            mCall.disconnect(disconnectTimeout);
                        }
                        return;
                    }

                    // If this call is already disconnected then we have nothing more to do.
                    if (mCall.isDisconnected()) {
                        Log.w(this, "Call has already been disconnected," +
                                " ignore the broadcast Call %s", mCall);
                        return;
                    }

                    // TODO: Remove the assumption that phone numbers are either SIP or TEL.
                    // This does not impact self-managed ConnectionServices as they do not use the
                    // NewOutgoingCallIntentBroadcaster.
                    Uri resultHandleUri = Uri.fromParts(
                            mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
                                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
                            resultNumber, null);

                    Uri originalUri = mIntent.getData();

                    if (originalUri.getSchemeSpecificPart().equals(resultNumber)) {
                        Log.v(this, "Call number unmodified after" +
                                " new outgoing call intent broadcast.");
                    } else {
                        Log.v(this, "Retrieved modified handle after outgoing call intent" +
                                        " broadcast: Original: %s, Modified: %s",
                                Log.pii(originalUri),
                                Log.pii(resultHandleUri));
                    }

                    GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
                    placeOutgoingCallImmediately(mCall, resultHandleUri, gatewayInfo,
                            mIntent.getBooleanExtra(
                                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false),
                            mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                                    VideoProfile.STATE_AUDIO_ONLY));
                }
            } finally {
                Trace.endSection();
                Log.endSession();
            }
        }
    }

    private void broadcastIntent(
            Intent originalCallIntent,
            String number,
            boolean receiverRequired,
            UserHandle targetUser) {
        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        if (number != null) {
            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
        }

        // Force receivers of this broadcast intent to run at foreground priority because we
        // want to finish processing the broadcast intent as soon as possible.
        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        Log.v(this, "Broadcasting intent: %s.", broadcastIntent);

        checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);

        final BroadcastOptions options = BroadcastOptions.makeBasic();
        options.setBackgroundActivityStartsAllowed(true);
        mContext.sendOrderedBroadcastAsUser(
                broadcastIntent,
                targetUser,
                android.Manifest.permission.PROCESS_OUTGOING_CALLS,
                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
                options.toBundle(),
                receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,
                null,  // scheduler
                Activity.RESULT_OK,  // initialCode
                number,  // initialData: initial value for the result data (number to be modified)
                null);  // initialExtras
    }

    private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,
                                              boolean speakerphoneOn, int videoState) {
        Log.i(this,
                "Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
        // Since we are not going to go through "Outgoing call broadcast", make sure
        // we mark it as ready.
        mCall.setNewOutgoingCallIntentBroadcastIsDone();
        mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
    }

3.NewOutgoingCallIntentBroadcaster 中会根据电话是否是紧急拨号决定是否简化拨号流程。 正常拨号是通过CallsManager 的 placeOutgoingCall 方法,通过 Call 对象方法 startCreateConnection 来创建连接

packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

    public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
                                  boolean speakerphoneOn, int videoState) {
        if (call == null) {
            // don't do anything if the call no longer exists
            Log.i(this, "Canceling unknown call.");
            return;
        }

        final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();

        if (gatewayInfo == null) {
            Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
        } else {
            Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
                    Log.pii(uriHandle), Log.pii(handle));
        }

        call.setHandle(uriHandle);
        call.setGatewayInfo(gatewayInfo);

        final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
                R.bool.use_speaker_when_docked);
        final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
        final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);

        // Auto-enable speakerphone if the originating intent specified to do so, if the call
        // is a video call, of if using speaker when docked
        PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(
                call.getTargetPhoneAccount(), call.getInitiatingUser());
        boolean allowVideo = false;
        if (account != null) {
            allowVideo = account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
        }
        call.setStartWithSpeakerphoneOn(speakerphoneOn || (useSpeakerForVideoCall && allowVideo)
                || (useSpeakerWhenDocked && useSpeakerForDock));
        call.setVideoState(videoState);

        if (speakerphoneOn) {
            Log.i(this, "%s Starting with speakerphone as requested", call);
        } else if (useSpeakerWhenDocked && useSpeakerForDock) {
            Log.i(this, "%s Starting with speakerphone because car is docked.", call);
        } else if (useSpeakerForVideoCall) {
            Log.i(this, "%s Starting with speakerphone because its a video call.", call);
        }

        if (call.isEmergencyCall()) {
            Executors.defaultThreadFactory().newThread(() ->
                    BlockedNumberContract.SystemContract.notifyEmergencyContact(mContext))
                    .start();
        }

        final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
        final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
                call.getTargetPhoneAccount());
        final String callHandleScheme =
                call.getHandle() == null ? null : call.getHandle().getScheme();
        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
            // If the account has been set, proceed to place the outgoing call.
            // Otherwise the connection will be initiated when the account is set by the user.
            if (call.isSelfManaged() && !isOutgoingCallPermitted) {
                if (call.isAdhocConferenceCall()) {
                    notifyCreateConferenceFailed(call.getTargetPhoneAccount(), call);
                } else {
                    notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
                }
            } else {
                if (call.isEmergencyCall()) {
                    // Drop any ongoing self-managed calls to make way for an emergency call.
                    disconnectSelfManagedCalls("place emerg call" /* reason */);
                }

                call.startCreateConnection(mPhoneAccountRegistrar);
            }
        } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
                requireCallCapableAccountByHandle ? callHandleScheme : null, false,
                call.getInitiatingUser()).isEmpty()) {
            // If there are no call capable accounts, disconnect the call.
            markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
                    "No registered PhoneAccounts"));
            markCallAsRemoved(call);
        }
    }

packages/services/Telecomm/src/com/android/server/telecom/Call.java

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        if (mCreateConnectionProcessor != null) {
            Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
                    " due to a race between NewOutgoingCallIntentBroadcaster and " +
                    "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
                    "invocation.");
            return;
        }
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    }


4.创建连接需通过 CreateConnectionProcessor来进行,首先选择PhoneAccount,接下来主要分为了两步:【1】创建ConnectionService,【2】通过ConnectionService创建 Connection。
具体步骤为使用 ConnectionServiceWrapper 来绑定 ConnectionService, 使用其子类 TelephonyConnectionService 来实现实际创建 Connection的 逻辑。Connection 也有许多子实现,首先确定拨打电话需要创建 TelephonyConnection,之后根据Phone 的type 创建出 GsmConnection或CdmaConnection

packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

    public void process() {
        Log.v(this, "process");
        clearTimeout();
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) {
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        }
        if (!mCall.isSelfManaged()) {
            adjustAttemptsForConnectionManager();
            adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
        }
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
    }

    private void attemptNextPhoneAccount() {
        Log.v(this, "attemptNextPhoneAccount");
        CallAttemptRecord attempt = null;
        if (mAttemptRecordIterator.hasNext()) {
            attempt = mAttemptRecordIterator.next();

            if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
                    attempt.connectionManagerPhoneAccount)) {
                Log.w(this,
                        "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for "
                                + "attempt: %s", attempt);
                attemptNextPhoneAccount();
                return;
            }

            // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it
            // also requires the BIND_TELECOM_CONNECTION_SERVICE permission.
            if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&
                    !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
                            attempt.targetPhoneAccount)) {
                Log.w(this,
                        "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for "
                                + "attempt: %s", attempt);
                attemptNextPhoneAccount();
                return;
            }
        }

        if (mCallResponse != null && attempt != null) {
            Log.i(this, "Trying attempt %s", attempt);
            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
            mService = mRepository.getService(phoneAccount.getComponentName(),
                    phoneAccount.getUserHandle());
            if (mService == null) {
                Log.i(this, "Found no connection service for attempt %s", attempt);
                attemptNextPhoneAccount();
            } else {
                mConnectionAttempt++;
                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);
                if (mCall.isIncoming()) {
                    if (mCall.isAdhocConferenceCall()) {
                        mService.createConference(mCall, CreateConnectionProcessor.this);
                    } else {
                        mService.createConnection(mCall, CreateConnectionProcessor.this);
                    }
                } else {
                    // Start to create the connection for outgoing call after the ConnectionService
                    // of the call has gained the focus.
                    mCall.getConnectionServiceFocusManager().requestFocus(
                            mCall,
                            new CallsManager.RequestCallback(new CallsManager.PendingAction() {
                                @Override
                                //ConnectionServiceFocusManager回调方法onRequestFocusDone会调用performAction
                                public void performAction() {
                                    if (mCall.isAdhocConferenceCall()) {
                                        Log.d(this, "perform create conference");
                                        mService.createConference(mCall,
                                                CreateConnectionProcessor.this);
                                    } else {
                                        Log.d(this, "perform create connection");
                                        //mService是ConnectionServiceWrapper对象,
                                        mService.createConnection(
                                                mCall,
                                                CreateConnectionProcessor.this);
                                    }
                                }
                            }));

                }
            }
        } else {
            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            if (mCall.isAdhocConferenceCall()) {
                notifyConferenceCallFailure(disconnectCause);
            } else {
                notifyCallConnectionFailure(disconnectCause);
            }
        }
    }

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

   public void createConnection(final Call call, final CreateConnectionResponse response) {
        Log.i(this, "createConnection(%s) via %s.", call, getComponentName());
        BindCallback callback = new BindCallback() {
            @Override
            public void onSuccess() {
                String callId = mCallIdMapper.getCallId(call);
                if (callId == null) {
                    Log.w(ConnectionServiceWrapper.this, "Call not present"
                            + " in call id mapper, maybe it was aborted before the bind"
                            + " completed successfully?");
                    response.handleCreateConnectionFailure(
                            new DisconnectCause(DisconnectCause.CANCELED));
                    return;
                }
                mPendingResponses.put(callId, response);

                GatewayInfo gatewayInfo = call.getGatewayInfo();
                Bundle extras = call.getIntentExtras();
                if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
                        gatewayInfo.getOriginalAddress() != null) {
                    extras = (Bundle) extras.clone();
                    extras.putString(
                            TelecomManager.GATEWAY_PROVIDER_PACKAGE,
                            gatewayInfo.getGatewayProviderPackageName());
                    extras.putParcelable(
                            TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
                            gatewayInfo.getOriginalAddress());
                }

                if (call.isIncoming() && mCallsManager.getEmergencyCallHelper()
                        .getLastEmergencyCallTimeMillis() > 0) {
                    // Add the last emergency call time to the connection request for incoming calls
                    if (extras == call.getIntentExtras()) {
                        extras = (Bundle) extras.clone();
                    }
                    extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
                            mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis());
                }

                // Call is incoming and added because we're handing over from another; tell CS
                // that its expected to handover.
                if (call.isIncoming() && call.getHandoverSourceCall() != null) {
                    extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
                    extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
                            call.getHandoverSourceCall().getTargetPhoneAccount());
                }

                Log.addEvent(call, LogUtils.Events.START_CONNECTION,
                        Log.piiHandle(call.getHandle()) + " via:" +
                                getComponentName().getPackageName());

                ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
                        .setAccountHandle(call.getTargetPhoneAccount())
                        .setAddress(call.getHandle())
                        .setExtras(extras)
                        .setVideoState(call.getVideoState())
                        .setTelecomCallId(callId)
                        // For self-managed incoming calls, if there is another ongoing call Telecom
                        // is responsible for showing a UI to ask the user if they'd like to answer
                        // this new incoming call.
                        .setShouldShowIncomingCallUi(
                                !mCallsManager.shouldShowSystemIncomingCallUi(call))
                        .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
                        .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
                        .build();

                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            connectionRequest,
                            call.shouldAttachToExistingConnection(),
                            call.isUnknown(),
                            Log.getExternalSession(TELECOM_ABBREVIATION));

                } catch (RemoteException e) {
                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                }
            }

            @Override
            public void onFailure() {
                Log.e(this, new Exception(), "Failure to call %s", getComponentName());
                response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
            }
        };

        //mBinder是ServiceBinder.Binder2的实例
        mBinder.bind(callback, call);
    }

packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java

public abstract class ServiceBinder {
    final class Binder2 {

        /**
         * Performs an asynchronous bind to the service (only if not already bound) and executes the
         * specified callback.
         *
         * @param callback The callback to notify of the binding's success or failure.
         * @param call     The call for which we are being bound.
         */
        void bind(BindCallback callback, Call call) {
            Log.d(ServiceBinder.this, "bind()");

            // Reset any abort request if we're asked to bind again.
            clearAbort();

            synchronized (mCallbacks) {
                if (!mCallbacks.isEmpty()) {
                    // Binding already in progress, append to the list of callbacks and bail out.
                    mCallbacks.add(callback);
                    return;
                }
                mCallbacks.add(callback);
            }

            if (mServiceConnection == null) {
                Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
                ServiceConnection connection = new ServiceBinderConnection(call);

                Log.addEvent(call, LogUtils.Events.BIND_CS, mComponentName);
                final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
                final boolean isBound;
                if (mUserHandle != null) {
                    isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,
                            mUserHandle);
                } else {
                    isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
                }
                if (!isBound) {
                    handleFailedConnection();
                    return;
                }
            } else {
                Log.d(ServiceBinder.this, "Service is already bound.");
                Preconditions.checkNotNull(mBinder);
                handleSuccessfulConnection();
            }
        }
    }

    private final class ServiceBinderConnection implements ServiceConnection {
        /**
         * The initial call for which the service was bound.
         */
        private Call mCall;

        ServiceBinderConnection(Call call) {
            mCall = call;
        }

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder binder) {
            try {
                Log.startSession("SBC.oSC", Log.getPackageAbbreviation(componentName));
                synchronized (mLock) {
                    Log.i(this, "Service bound %s", componentName);

                    Log.addEvent(mCall, LogUtils.Events.CS_BOUND, componentName);

                    // Unbind request was queued so unbind immediately.
                    if (mIsBindingAborted) {
                        clearAbort();
                        logServiceDisconnected("onServiceConnected");
                        mContext.unbindService(this);
                        handleFailedConnection();
                        return;
                    }
                    if (binder != null) {
                        mServiceDeathRecipient = new ServiceDeathRecipient(componentName);
                        try {
                            binder.linkToDeath(mServiceDeathRecipient, 0);
                            mServiceConnection = this;
                            setBinder(binder);    //保存绑定服务成功后的binder对象
                            handleSuccessfulConnection();    //bind成功后处理
                        } catch (RemoteException e) {
                            Log.w(this, "onServiceConnected: %s died.");
                            if (mServiceDeathRecipient != null) {
                                mServiceDeathRecipient.binderDied();
                            }
                        }
                    }
                }
            } finally {
                Log.endSession();
            }
        }
    }

    private void setBinder(IBinder binder) {
        if (mBinder != binder) {
            if (binder == null) {
                removeServiceInterface();
                mBinder = null;
                for (Listener l : mListeners) {
                    l.onUnbind(this);
                }
            } else {
                mBinder = binder;
                setServiceInterface(binder);
            }
        }
    }
}

protected abstract void setServiceInterface(IBinder binder);   //抽象方法,在ConnectionServiceWrapper实现。

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

    protected void setServiceInterface(IBinder binder) {
        mServiceInterface = IConnectionService.Stub.asInterface(binder);
        Log.v(this, "Adding Connection Service Adapter.");
        addConnectionServiceAdapter(mAdapter);
    }


    private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
        if (isServiceValid("addConnectionServiceAdapter")) {
            try {
                logOutgoing("addConnectionServiceAdapter %s", adapter);
                mServiceInterface.addConnectionServiceAdapter(adapter, Log.getExternalSession());
            } catch (RemoteException e) {
            }
        }
    }

frameworks/base/telecomm/java/android/telecom/ConnectionService.java

public abstract class ConnectionService extends Service {
    private final IBinder mBinder = new IConnectionService.Stub() {
        @Override
        public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter,
                                                Session.Info sessionInfo) {
            Log.startSession(sessionInfo, SESSION_ADD_CS_ADAPTER);
            try {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = adapter;
                args.arg2 = Log.createSubsession();
                mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, args).sendToTarget();
            } finally {
                Log.endSession();
            }
        }
    }
    
    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ADD_CONNECTION_SERVICE_ADAPTER: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    try {
                        IConnectionServiceAdapter adapter = (IConnectionServiceAdapter) args.arg1;
                        Log.continueSession((Session) args.arg2,
                                SESSION_HANDLER + SESSION_ADD_CS_ADAPTER);
                        mAdapter.addAdapter(adapter);
                        onAdapterAttached();
                    } finally {
                        args.recycle();
                        Log.endSession();
                    }
                    break;
                    ...
                }
            }
        }
    }
}

frameworks/base/telecomm/java/android/telecom/ConnectionServiceAdapter.java

    void addAdapter(IConnectionServiceAdapter adapter) {
        for (IConnectionServiceAdapter it : mAdapters) {
            if (it.asBinder() == adapter.asBinder()) {
                Log.w(this, "Ignoring duplicate adapter addition.");
                return;
            }
        }
        if (mAdapters.add(adapter)) {
            try {
                adapter.asBinder().linkToDeath(this, 0);
            } catch (RemoteException e) {
                mAdapters.remove(adapter);
            }
        }
    }

packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java

    private void handleSuccessfulConnection() {
        // Make a copy so that we don't have a deadlock inside one of the callbacks.
        Set<BindCallback> callbacksCopy = new ArraySet<>();
        synchronized (mCallbacks) {
            callbacksCopy.addAll(mCallbacks);
            mCallbacks.clear();
        }

        for (BindCallback callback : callbacksCopy) {
            callback.onSuccess();
        }
    }

BindCallback匿名实现在ConnectionServiceWrapper中的createConnection方法中。

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

    public void createConnection(final Call call, final CreateConnectionResponse response) {
        Log.i(this, "createConnection(%s) via %s.", call, getComponentName());
        BindCallback callback = new BindCallback() {
            @Override
            public void onSuccess() {
                String callId = mCallIdMapper.getCallId(call);
                ...

                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            connectionRequest,
                            call.shouldAttachToExistingConnection(),
                            call.isUnknown(),
                            Log.getExternalSession(TELECOM_ABBREVIATION));

                } catch (RemoteException e) {
                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                }
            }
        };

        mBinder.bind(callback, call);
    }

frameworks/base/telecomm/java/android/telecom/ConnectionService.java

public abstract class ConnectionService extends Service {
        private final IBinder mBinder = new IConnectionService.Stub() {

            public void createConnection(
                    PhoneAccountHandle connectionManagerPhoneAccount,
                    String id,
                    ConnectionRequest request,
                    boolean isIncoming,
                    boolean isUnknown,
                    Session.Info sessionInfo) {
                Log.startSession(sessionInfo, SESSION_CREATE_CONN);
                try {
                    SomeArgs args = SomeArgs.obtain();
                    args.arg1 = connectionManagerPhoneAccount;
                    args.arg2 = id;
                    args.arg3 = request;
                    args.arg4 = Log.createSubsession();
                    args.argi1 = isIncoming ? 1 : 0;
                    args.argi2 = isUnknown ? 1 : 0;
                    mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
                } finally {
                    Log.endSession();
                }
            }

        }

        private final Handler mHandler = new Handler(Looper.getMainLooper()) {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_CREATE_CONNECTION: {
                        SomeArgs args = (SomeArgs) msg.obj;
                        Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
                        try {
                            final PhoneAccountHandle connectionManagerPhoneAccount =
                                    (PhoneAccountHandle) args.arg1;
                            final String id = (String) args.arg2;
                            final ConnectionRequest request = (ConnectionRequest) args.arg3;
                            final boolean isIncoming = args.argi1 == 1;
                            final boolean isUnknown = args.argi2 == 1;
                            if (!mAreAccountsInitialized) {
                            ...
                            } else {
                                //调用onCreateOutgoingConnection创建Connection
                                createConnection(
                                        connectionManagerPhoneAccount,
                                        id,
                                        request,
                                        isIncoming,
                                        isUnknown);
                            }
                        } finally {
                            args.recycle();
                            Log.endSession();
                        }
                        break;
                    }
                }
            }
        }

        protected void createConnection(
                final PhoneAccountHandle callManagerAccount,
                final String callId,
                final ConnectionRequest request,
                boolean isIncoming,
                boolean isUnknown) {
        ...
            if (isHandover) {
            ...
            } else {
                connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
                        : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
                        : onCreateOutgoingConnection(callManagerAccount, request);
            }
            Log.d(this, "createConnection, connection: %s", connection);
            if (connection == null) {
                Log.i(this, "createConnection, implementation returned null connection.");
                connection = Connection.createFailedConnection(
                        new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
            } else {
            ...
            }
        
        ...
            if (isUnknown) {
                triggerConferenceRecalculate();
            }
        }
    }

packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java

public class TelephonyConnectionService extends ConnectionService {
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) {
        Log.i(this, "onCreateOutgoingConnection, request: " + request);
        ...
        if (needToTurnOnRadio) {
            ...
        } else {
            ...
            if (!isEmergencyNumber) {
                final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                        false, handle, phone);
                if (isAdhocConference) {
                    if (resultConnection instanceof TelephonyConnection) {
                        TelephonyConnection conn = (TelephonyConnection) resultConnection;
                        conn.setParticipants(request.getParticipants());
                    }
                    return resultConnection;
                } else {
                    return placeOutgoingConnection(request, resultConnection, phone);
                }
            } else {
                ...
            }
        }
    }
    
    private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
                                              boolean isEmergencyNumber, final Uri handle, Phone phone) {

        ...

        PhoneAccountHandle accountHandle = adjustAccountHandle(phone, request.getAccountHandle());
        //根据phone的gsm/cdma/sip类型创建对应的connection.im不在这里创建。
        final TelephonyConnection connection =
                createConnectionFor(phone, null, true /* isOutgoing */, accountHandle,
                        request.getTelecomCallId(), request.isAdhocConferenceCall());
        if (connection == null) {
            return Connection.createFailedConnection(
                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.OUTGOING_FAILURE,
                            "Invalid phone type",
                            phone.getPhoneId()));
        }
        ...
        return connection;
    }

    private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
        ...
        final com.android.internal.telephony.Connection originalConnection;
        try {
            if (phone != null) {
                EmergencyNumber emergencyNumber =
                        phone.getEmergencyNumberTracker().getEmergencyNumber(number);
                ...
                //调用GsmCdmaPhone.dial()
                originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
                                .setVideoState(videoState)
                                .setIntentExtras(extras)
                                .setRttTextStream(connection.getRttTextStream())
                                .build(),
                        // We need to wait until the phone has been chosen in GsmCdmaPhone to
                        // register for the associated TelephonyConnection call event listeners.
                        connection::registerForCallEvents);
            } else {
                originalConnection = null;
            }
        } catch (CallStateException e) {
            Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
            connection.unregisterForCallEvents();
            handleCallStateException(e, connection, phone);
            return;
        }
        ...
    }
}


5.Connection 创建完毕, TelephonyConnectionService 调用 placeOutgoingConnection 方法,通过Phone 对象调用 dial()方法进行拨号,其实调用的是执行顺序是GsmCdmaPhone.dial(),在GsmCdmaPhone 对象的拨号方法中会判断 ImsPhone对象是否存在,并根据相关配置决定是否要用ImsPhone对象来进行拨号 ;

 frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

public class GsmCdmaPhone extends Phone {
    public Connection dial(String dialString, @NonNull DialArgs dialArgs,
                           Consumer<Phone> chosenPhoneConsumer) throws CallStateException {
        ...
        if (imsPhone != null && !allowWpsOverIms && !useImsForCall && isWpsCall
                && imsPhone.getCallTracker() instanceof ImsPhoneCallTracker) {
            logi("WPS call placed over CS; disconnecting all IMS calls..");
            ImsPhoneCallTracker tracker = (ImsPhoneCallTracker) imsPhone.getCallTracker();
            tracker.hangupAllConnections();
        }

        if ((useImsForCall && (!isMmiCode || isPotentialUssdCode))
                || (isMmiCode && useImsForUt)
                || useImsForEmergency) {
            try {
                if (DBG) logd("Trying IMS PS call");
                chosenPhoneConsumer.accept(imsPhone);
                //调用ImsPhone.dial();
                return imsPhone.dial(dialString, dialArgs);
            } catch (CallStateException e) {
                if (DBG) logd("IMS PS call exception " + e +
                        "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
                // Do not throw a CallStateException and instead fall back to Circuit switch
                // for emergency calls and MMI codes.
                if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
                    logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
                            + "to CS.");
                } else {
                    CallStateException ce = new CallStateException(e.getError(), e.getMessage());
                    ce.setStackTrace(e.getStackTrace());
                    throw ce;
                }
            }
        }
        ...
    }
}

ImsPhone.dial()-> ImsPhone.dialInternal() -> ImsPhoneCallTracker.dial() -> ImsPhoneCallTracker.dialInternal

frameworks/opt/net/ims/src/java/com/android/ims/ImsManager.java

mImsManager.makeCall()

    public ImsCall makeCall(ImsCallProfile profile, String[] callees,
            ImsCall.Listener listener) throws ImsException {
        if (DBG) {
            log("makeCall :: profile=" + profile);
        }

        // Check we are still alive
        getOrThrowExceptionIfServiceUnavailable();

        ImsCall call = new ImsCall(mContext, profile);

        call.setListener(listener);
        ImsCallSession session = createCallSession(profile);

        if ((callees != null) && (callees.length == 1) && !(session.isMultiparty())) {
            call.start(session, callees[0]);
        } else {
            call.start(session, callees);
        }

        return call;
    }

​​​​​​​frameworks/base/telephony/java/android/telephony/ims/ImsCallSession.java

    public void start(String callee, ImsCallProfile profile) {
        if (mClosed) {
            return;
        }

        try {
            miSession.start(callee, profile);
        } catch (RemoteException e) {
        }
    }

frameworks/base/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java

    public void start(String callee, ImsCallProfile profile) {
            executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
        }

​​​​​​​
6.之后就是 GsmCdmaPhoneCallTracker / ImsPhoneCallTracker 对象与底层交互的流程。【1】如果是使用 GsmCdmaPhone对象拨号,GsmCdmaPhoneCallTracker中会与 RILJ 交互;【2】如果是使用ImsPhone对象拨号,则需要通过 ImsManager 调用到各个平台自行实现的 Ims.apk来完成拨号流程

电话挂断初步排查

在notepad中用查找模式为正则表达式的方式搜索一下信息:

placeCall|termina|onDisconnect|DisconnectCause|InCallAda|HANGUP|REQUEST_DISCONNECT|DIALING_ENDED|BOUNCE_HANG_UP_BUTTON_PRESSED|incallada

底层挂断,modem检查

呼出未接通,手动挂断

来电没接听,对端挂断

未呼出,网络问题,需要对比测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值