转载请注明出处:https://blog.csdn.net/turtlejj/article/details/81240892,谢谢~

 

       由于工作中需要熟悉Android拨打电话的完整流程,特将学习的过程记录下来,以便将来进行回顾,同时也欢迎大家对文章中不正确的地方加以指正。

       在代码中,我在关键地方都添加了自己的对于代码理解的中文注释,已方便更好的理解代码的含义,以下就开始我们对拨号流程的梳理。

 

一、在拨号盘Dialer中点击拨号按钮

/packages/apps/Dialer/java/com/android/dialer/app/dialpad/DialpadFragment.java

按下拨号按钮后,会调用handleDialButtonPressed()方法

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) {        keyPressed(KeyEvent.KEYCODE_DEL);    } else if (resId == R.id.digits) {        if (!isDigitsEmpty()) {            mDigits.setCursorVisible(true);        }    } else if (resId == R.id.dialpad_overflow) {        mOverflowPopupMenu.show();    } else {        LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);        return;    }}

在handleDialButtonPressed()方法中,创建intent,并调用DialerUtils的startActivityWithErrorToast()方法

private void handleDialButtonPressed() {  if (isDigitsEmpty()) { // 如果没有输入号码    handleDialButtonClickWithEmptyDigits();  } else {    final String number = mDigits.getText().toString();    // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated    // test equipment.    // TODO: clean it up.    // 如果输入的号码为禁止拨打的号码    if (number != null        && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)        && number.matches(mProhibitedPhoneNumberRegexp)) {      LogUtil.i(           "DialpadFragment.handleDialButtonPressed",           "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 =          new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();      DialerUtils.startActivityWithErrorToast(getActivity(), intent);      hideAndClearDialpad(false);    }  }}

/packages/apps/Dialer/java/com/android/dialer/callintent/CallintentBuilder.java

创建intent的具体流程如下

public CallIntentBuilder(@NonNull String number, CallInitiationType.Type callInitiationType) {  // 调用CallUtil的getCallUri()方法,将号码封装成Uri    this(CallUtil.getCallUri(Assert.isNotNull(number)), callInitiationType);}->public static Uri getCallUri(String number) {    if (PhoneNumberHelper.isUriNumber(number)) {  // 网络电话流程      return Uri.fromParts(PhoneAccount.SCHEME_SIP, number, null);    }    // 普通电话流程    return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);}->public CallIntentBuilder(@NonNull Uri uri, CallInitiationType.Type callInitiationType) {    // 调用CcreateCallSpecificAppData()方法,对callInitiationType进行转换    this(uri, createCallSpecificAppData(callInitiationType));}->private static @NonNull CallSpecificAppData createCallSpecificAppData(      CallInitiationType.Type callInitiationType) {    CallSpecificAppData callSpecificAppData = CallSpecificAppData.newBuilder().setCallInitiationType(callInitiationType).build();    return callSpecificAppData;}->public Intent build() {    // 设置intent的action为ACTION_CALL    Intent intent = new Intent(Intent.ACTION_CALL, uri);    // 普通电话为VideoProfile.STATE_AUDIO_ONLY    intent.putExtra(        TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,        isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);    Bundle extras = new Bundle();    extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());    CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData);    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);    // 由于没有设置PhoneAccountHandle,因此为null    if (phoneAccountHandle != null) {      intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);    }    if (!TextUtils.isEmpty(callSubject)) {      intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);    }    return intent;}

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

intent创建完成后,将其传入DialerUtils的startActivityWithErrorToast()方法中,并调用placeCallOrMakeToast()方法

public static void startActivityWithErrorToast(Context context, Intent intent) {    startActivityWithErrorToast(context, intent, R.string.activity_not_available);}public static void startActivityWithErrorToast(      final Context context, final Intent intent, int msgId) {    try {      // action为ACTION_CALL,进入      if ((Intent.ACTION_CALL.equals(intent.getAction()))) {        ......        // 不会弹出警告,进入else分支        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();    }}

调用TelecomUtil的placeCall()方法,判断是否拥有呼出电话的权限,如果有,则继续流程;否则,将弹出Toast提示无权限

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();    }}

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

调用hasCallPhonePermission()方法判断是否具有Manifest.permission.CALL_PHONE权限,如果有,则调用TelecomManager中的placeCall()方法继续处理通话流程

public static boolean placeCall(Context context, Intent intent) {    if (hasCallPhonePermission(context)) {      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());      return true;    }    return false;}

二、Telecom处理通话流程

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

在placeCall()方法中,调用了ITelecomService接口中的placeCall()方法,而ITelecomService接口中的placeCall()方法在TelecomServiceImpl.java中被实现(此处用到了一些AIDL的知识,不了解的同学可以自行学习一下)

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());        } catch (RemoteException e) {            Log.e(TAG, "Error calling ITelecomService#placeCall", e);        }    }}

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

调用UserCallIntentProcessor的processIntent()方法

public void placeCall(Uri handle, Bundle extras, String callingPackage) {    try {        Log.startSession("TSI.pC");        enforceCallingPackage(callingPackage);        PhoneAccountHandle phoneAccountHandle = null;        if (extras != null) {            // 由于没有设置PhoneAccountHandle,因此PhoneAccountHandle变量为null            phoneAccountHandle = extras.getParcelable(                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);        }        // 由于PhoneAccountHandle变量为null,因此isSelfManaged变量为false        boolean isSelfManaged = phoneAccountHandle != null &&                isSelfManagedConnectionService(phoneAccountHandle);        if (isSelfManaged) {            ......        } else if (!canCallPhone(callingPackage, "placeCall")) {            throw new SecurityException("Package " + callingPackage                    + " is not allowed to place phone calls");        }        // Note: we can still get here for the default/system dialer, even if the Phone        // permission is turned off. This is because the default/system dialer is always        // allowed to attempt to place a call (regardless of permission state), in case        // it turns out to be an emergency call. If the permission is denied and the        // call is being made to a non-emergency number, the call will be denied later on        // by {@link UserCallIntentProcessor}.        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 {                // 新创建一个intent对象,并设置action为Intent.ACTION_CALL                final Intent intent = new Intent(Intent.ACTION_CALL, handle);                if (extras != null) {                    extras.setDefusable(true);                    intent.putExtras(extras);                }                // mUserCallIntentProcessorFactory.create()方法返回的是一个UserCallIntentProcessor对象                mUserCallIntentProcessorFactory.create(mContext, userHandle)                        .processIntent(                                intent, callingPackage, isSelfManaged ||                                        (hasCallAppOp && hasCallPermission));            } finally {                Binder.restoreCallingIdentity(token);            }        }    } finally {        Log.endSession();    }}

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

调用processOutgoingCallIntent()方法

public void processIntent(Intent intent, String callingPackageName,        boolean canCallNonEmergency) {    // 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);    }}

调用sendBroadcastToReceiver()方法

private void processOutgoingCallIntent(Intent intent, String callingPackageName,        boolean canCallNonEmergency) {    Uri handle = intent.getData();                        // tel:13012123434    String scheme = handle.getScheme();                   // tel    String uriString = handle.getSchemeSpecificPart();    // 13012123434    if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {        handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?                PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);    }    // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this check a managed    // profile user because this check can always be bypassed by copying and pasting the phone    // number into the personal dialer.    if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {        // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS        // restriction.        ......    }    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);    intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,            isDefaultOrSystemDialer(callingPackageName));    // Save the user handle of current user before forwarding the intent to primary user.    intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);    sendBroadcastToReceiver(intent);}

设置广播接收者为PrimaryCallReveiver.class,并发送广播

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");    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);    return true;}

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

调用CallIntentProcessor的processIntent()方法

public void onReceive(Context context, Intent intent) {    Log.startSession("PCR.oR");    synchronized (getTelecomSystem().getLock()) {        // getCallIntentProcessor()方法返回一个CallIntentProcessor对象        getTelecomSystem().getCallIntentProcessor().processIntent(intent);    }    Log.endSession();}

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

调用processOutgoingCallIntent()方法

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();}

processOutgoingCallIntent()方法做了两件事:

第一步,调用CallsManager的startOutgoingCall()方法

第二步,调用sendNewOutgoingCallIntent()方法,将第一步中创建的Call对象传入,以继续呼叫流程

static void processOutgoingCallIntent(        Context context,        CallsManager callsManager,        Intent intent) {    Uri handle = intent.getData();                       // tel:13012123434    String scheme = handle.getScheme();                  // tel    String uriString = handle.getSchemeSpecificPart();   // 13012123434    if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {        handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?                PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);    }    PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);    Bundle clientExtras = null;    if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {        clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);    }    if (clientExtras == null) {        clientExtras = new Bundle();    }    // Ensure call subject is passed on to the connection service.    if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {        String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);        clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);    }    final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,            VideoProfile.STATE_AUDIO_ONLY);    clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);    boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);    // Show the toast to warn user that it is a personal call though initiated in work profile.    if (fixedInitiatingUser) {        Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();    }    UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);    // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns    Call call = callsManager            .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,                    intent);    if (call != null) {        sendNewOutgoingCallIntent(context, call, callsManager, intent);    }}

我们先来说一说第一步,调用CallsManager的startOutgoingCall()方法,该方法做了两件事情:

1. 建立一个Call对象

2. 调用addCall()方法

 

这里涉及了PhoneAccount和PhoneAccountHandle的概念,对于这两个概念,这里先简单地介绍一下。如果想更深入的了解,请看我的另一篇文章《Android 8.0 PhoneAccount详解》。

 

最容易理解的,Android系统会为每一张Sim卡建立一个PhoneAccount,也会为网络电话建立PhoneAccount,在手机不插卡时,还会建立一个用于拨打紧急呼叫的PhoneAccount。而每一个PhoneAccount都含有一个PhoneAccountHandle,PhoneAccountHandle是每一个PhoneAccount的唯一标识。

显而易见,最常见的情况下,defaultPhoneAccountHandle就是手机在插入两张Sim卡时,系统默认用于拨打电话的那一个PhoneAccountHandle。

 

有了以上的概念,我们来大致看一下Call对象是如何创建的

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

Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,        UserHandle initiatingUser, Intent originalIntent) {    boolean isReusedCall = true;    Call call = reuseOutgoingCall(handle);  // 查看是否有可复用的Call对象    // 由于phoneAccountHandle变量为null,因此无法获得PhoneAccount对象,accout变量为null    PhoneAccount account =            mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);    // Create a call with original handle. The handle may be changed when the call is attached    // to a connection service, but in most cases will remain the same.    if (call == null) {        // 新建一个Call对象        call = new Call(getNextCallId(), mContext,                this,                mLock,                mConnectionServiceRepository,                mContactsAsyncHelper,                mCallerInfoAsyncQueryFactory,                mPhoneNumberUtilsAdapter,                handle,                null /* gatewayInfo */,                null /* connectionManagerPhoneAccount */,                null /* phoneAccountHandle */,                Call.CALL_DIRECTION_OUTGOING /* callDirection */,                false /* forceAttachToExistingConnection */,                false /* isConference */        );        call.initAnalytics();        // Ensure new calls related to self-managed calls/connections are set as such.  This        // will be overridden when the actual connection is returned in startCreateConnection,        // however doing this now ensures the logs and any other logic will treat this call as        // self-managed from the moment it is created.        // 第三方通话应用一般是SelfManaged的,我们这里只关心Android系统的Dialer,所以该值为false        if (account != null) {            call.setIsSelfManaged(account.isSelfManaged());            if (call.isSelfManaged()) {                // Self-managed calls will ALWAYS use voip audio mode.                call.setIsVoipAudioMode(true);            }        }        call.setInitiatingUser(initiatingUser);        isReusedCall = false;    }    if (extras != null) {        // Set the video state on the call early so that when it is added to the InCall UI the        // UI knows to configure itself as a video call immediately.        // videoState为VideoProfile.STATE_AUDIO_ONLY        int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,                VideoProfile.STATE_AUDIO_ONLY);        // If this is an emergency video call, we need to check if the phone account supports        // emergency video calling.        // Also, ensure we don't try to place an outgoing call with video if video is not        // supported.        if (VideoProfile.isVideo(videoState)) {            ......        }        call.setVideoState(videoState);    }    // targetPhoneAccount为null    PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(            phoneAccountHandle, initiatingUser);    // isSelfManaged为false    boolean isSelfManaged = targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();    List accounts;    if (!isSelfManaged) {        accounts = constructPossiblePhoneAccounts(handle, initiatingUser);        Log.v(this, "startOutgoingCall found accounts = " + accounts);        // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this        // call as if a phoneAccount was not specified (does the default behavior instead).        // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.        if (phoneAccountHandle != null) {            if (!accounts.contains(phoneAccountHandle)) {                phoneAccountHandle = null;            }        }        if (phoneAccountHandle == null && accounts.size() > 0) {            // No preset account, check if default exists that supports the URI scheme for the            // handle and verify it can be used.            if (accounts.size() > 1) {  // 如果已注册的的PhoneAccountHandle数大于1                // 获取用于拨打电话的默认PhoneAccountHandle对象                PhoneAccountHandle defaultPhoneAccountHandle =                        mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(                                handle.getScheme(), initiatingUser);                // 获取到的PhoneAccountHandle对象不为null,且其包含在已注册列表中                if (defaultPhoneAccountHandle != null &&                        accounts.contains(defaultPhoneAccountHandle)) {                    phoneAccountHandle = defaultPhoneAccountHandle;                }            } else {  // 已注册的PhoneAccountHandle数为1,则选择该PhoneAccountHandle                // Use the only PhoneAccount that is available                phoneAccountHandle = accounts.get(0);            }        }    } else {        accounts = Collections.EMPTY_LIST;    }    // 使用获取到的PhoneAccountHandle对象,设置Call对象的PhoneAccount    call.setTargetPhoneAccount(phoneAccountHandle);    boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;    // Do not support any more live calls.  Our options are to move a call to hold, disconnect    // a call, or cancel this call altogether. If a call is being reused, then it has already    // passed the makeRoomForOutgoingCall check once and will fail the second time due to the    // call transitioning into the CONNECTING state.    if (!isSelfManaged && !isPotentialInCallMMICode && (!isReusedCall &&            !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {        ......    }    boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&            !call.isEmergencyCall() && !isSelfManaged;    if (needsAccountSelection) {        // 如果前面选择PhoneAccountHandle的时候,已注册的的PhoneAccountHandle数大于1        // 但未设置拨打电话的默认PhoneAccountHandle        // This is the state where the user is expected to select an account        call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");        // Create our own instance to modify (since extras may be Bundle.EMPTY)        extras = new Bundle(extras);        extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);    } else {        call.setState(                CallState.CONNECTING,                phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());        PhoneAccount accountToUse =                mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);        if (extras != null                && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {            if (accountToUse != null                    && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {                call.setRttStreams(true);            }        }    }    setIntentExtrasAndStartTime(call, extras);    if ((isPotentialMMICode(handle) || isPotentialInCallMMICode)            && !needsAccountSelection) {        ......    } else if (!isSelfManaged && hasSelfManagedCalls() && !call.isEmergencyCall()) {        ......    } else if (!mCalls.contains(call)) {        // We check if mCalls already contains the call because we could potentially be reusing        // a call which was previously added (See {@link #reuseOutgoingCall}).        addCall(call);    }    return call;}

上面的代码中,有一个比较关键的constructPossiblePhoneAccounts()方法,我们来看一下他的实现

// Construct the list of possible PhoneAccounts that the outgoing call can use based on the// active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,// then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.private List constructPossiblePhoneAccounts(Uri handle, UserHandle user) {    if (handle == null) {        return Collections.emptyList();    }    // 获取可以用于拨打电话的PhoneAccountHandle的列表    List allAccounts =            mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user);    // First check the Radio SIM Technology    // 获取手机所支持的模式,单卡、双卡单通还是双卡双通等    if(mRadioSimVariants == null) {        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(                Context.TELEPHONY_SERVICE);        // Cache Sim Variants        mRadioSimVariants = tm.getMultiSimConfiguration();    }    // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount    // Should be available if a call is already active on the SIM account.    if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) {        List simAccounts =                mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser();        PhoneAccountHandle ongoingCallAccount = null;        for (Call c : mCalls) {            if (!c.isDisconnected() && !c.isNew() && simAccounts.contains(                    c.getTargetPhoneAccount())) {                ongoingCallAccount = c.getTargetPhoneAccount();                break;            }        }        if (ongoingCallAccount != null) {            // Remove all SIM accounts that are not the active SIM from the list.            simAccounts.remove(ongoingCallAccount);            allAccounts.removeAll(simAccounts);        }    }    return allAccounts;}

在获取了PhoneAccountHandle的列表以后,我们回到刚刚startOutgoingCall()方法中。

当返回PhoneAccountHandle数大于一个时,则选择defaultPhoneAccountHandle来拨打电话;当返回PhoneAccountHandle数为一时,理所当然,只能选择这个PhoneAccountHandle。

在选好PhoneAccountHandle后,由于PhoneAccountHandle是PhoneAccount的唯一标识,因此我们也就得到了用于拨打电话的PhoneAccount。

此后,将Call对象的状态置为CONNECTING,我们的Call对象就建立完成了。

 

接下来,我们分析addCall()方法

private void addCall(Call call) {    Trace.beginSection("addCall");    Log.v(this, "addCall(%s)", call);    call.addListener(this);    // 将新建的Call对象添加到mCalls列表中,以方便对所有Call对象进行管理    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();    // onCallAdded for calls which immediately take the foreground (like the first call).    // 调用mListeners中所有listener的onCallAdded()方法    for (CallsManagerListener listener : mListeners) {        if (LogUtils.SYSTRACE_DEBUG) {            Trace.beginSection(listener.getClass().toString() + " addCall");        }        listener.onCallAdded(call);        if (LogUtils.SYSTRACE_DEBUG) {            Trace.endSection();        }    }    Trace.endSection();}

在CallsManager的构造函数中,我们可以看到

mListeners.add(mInCallWakeLockController);mListeners.add(statusBarNotifier);mListeners.add(mCallLogManager);mListeners.add(mPhoneStateBroadcaster);mListeners.add(mInCallController);mListeners.add(mCallAudioManager);mListeners.add(missedCallNotifier);mListeners.add(mHeadsetMediaButton);mListeners.add(mProximitySensorManager);

这里就不展开讲每一个onCallAdded()方法具体做了些什么事情了,代码不难理解,大家感兴趣的话,可以自己看一看。

 

其中InCallController的onCallAdded()方法会通过一系列的步骤启动InCallUi,由于这里产生了分支,在同一篇文章中阐述容易造成混乱,因此我会在后续的文章中再详细介绍启动InCallUi的代码部分。

 

那么上面说了,processOutgoingCallIntent()方法做了两件事,第一步调用CallsManager的startOutgoingCall()方法建立Call对象并调用addCall()方法,这部分已经说完了。我们继续来说一说第二步,调用sendNewOutgoingCallIntent()方法。

 

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

从代码中可以看出,我们建立了一个NewOutgoingCallIntentBroadcaster对象,并调用了其processIntent()方法

static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,        Intent intent) {    // 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.    final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);    NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(            context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),            isPrivilegedDialer);    final int result = broadcaster.processIntent();    final boolean success = result == DisconnectCause.NOT_DISCONNECTED;    if (!success && call != null) {        disconnectCallAndShowErrorDialog(context, call, result);    }}

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

我们这里只分析普通呼叫流程的代码,暂不讨论Voicemail和紧急呼叫的情况,请看代码中的中文注释

经过一系列判断,最后会调用broadcastIntent()方法

public int processIntent() {    Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");    Intent intent = mIntent;    String action = intent.getAction();    final Uri handle = intent.getData();    if (handle == null) {        Log.w(this, "Empty handle obtained from the call intent.");        return DisconnectCause.INVALID_NUMBER;    }    // 正常呼叫流程下,该变量为false    boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());    if (isVoicemailNumber) {        ......    }    // 获取电话号码    String number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);    if (TextUtils.isEmpty(number)) {        Log.w(this, "Empty number obtained from the call intent.");        return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;    }    boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);    if (!isUriNumber) {        number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);        number = mPhoneNumberUtilsAdapter.stripSeparators(number);    }    // 判断是否是潜在的紧急号码,此处为false    final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);    Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);    rewriteCallIntentAction(intent, isPotentialEmergencyNumber);    action = intent.getAction();    // True for certain types of numbers that are not intended to be intercepted or modified    // by third parties (e.g. emergency numbers).    boolean callImmediately = false;    if (Intent.ACTION_CALL.equals(action)) {        if (isPotentialEmergencyNumber) {            ......        }    } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {        ......    } else {        Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);        return DisconnectCause.INVALID_NUMBER;    }    // True for all managed calls, false for self-managed calls.    boolean sendNewOutgoingCallBroadcast = true;    PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra(            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);    if (targetPhoneAccount != null) {        PhoneAccount phoneAccount =                mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked(                        targetPhoneAccount);        // 前面提到过,第三方通话应用一般为SelfManaged的,而系统应用不是        if (phoneAccount != null && phoneAccount.isSelfManaged()) {            callImmediately = true;            sendNewOutgoingCallBroadcast = false;            Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");        }    }    // callImmediately为false    if (callImmediately) {        ......    }    if (sendNewOutgoingCallBroadcast) {        UserHandle targetUser = mCall.getInitiatingUser();        Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);        broadcastIntent(intent, number, !callImmediately, targetUser);    }    return DisconnectCause.NOT_DISCONNECTED;}

broadcastIntent()方法会调用Android系统的sendOrderedBroadcastAsUser()方法发送广播

由于调用时,第三个参数传递的值为true,因此,NewOutgoingCallBroadcastIntentReceiver类会在其onReceive()方法中对广播进行处理

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);    mContext.sendOrderedBroadcastAsUser(            broadcastIntent,            targetUser,            android.Manifest.permission.PROCESS_OUTGOING_CALLS,            AppOpsManager.OP_PROCESS_OUTGOING_CALLS,            receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,            null,  // scheduler            Activity.RESULT_OK,  // initialCode            number,  // initialData: initial value for the result data (number to be modified)            null);  // initialExtras}

NewOutgoingCallBroadcastIntentReceiver是NewOutgoingCallIntentBroadcaster的子类,其onReceive()方法代码如下

在代码的最后,会调用CallsManager的placeOutgoingCall()方法

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));            ......            Uri resultHandleUri = Uri.fromParts(                    mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?                            PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,                    resultNumber, null);            ......            GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);            mCall.setNewOutgoingCallIntentBroadcastIsDone();            mCallsManager.placeOutgoingCall(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();    }}

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

在对是否需要开启扬声器(Speaker)、是否为紧急呼叫以及PhoneAccount是否可用于拨打电话进行判断后,调用Call对象的startCreateConnection()方法

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);    // 这里判断是否要开启扬声器(Speaker),我们普通的电话默认是使用听筒的(Earpiece)    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    call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall            || (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()) {        new AsyncEmergencyContactNotifier(mContext).execute();    }    final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(            com.android.internal.R.bool.config_requireCallCapableAccountForHandle);    final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,            call.getTargetPhoneAccount());    // 进入这里    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) {            notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);        } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {            markCallDisconnectedDueToSelfManagedCall(call);        // 进入这里,调用Call对象的startCreateConnection()方法        } else {            if (call.isEmergencyCall()) {                // Disconnect all self-managed calls to make priority for emergency call.                disconnectSelfManagedCalls();            }            call.startCreateConnection(mPhoneAccountRegistrar);        }    } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(            requireCallCapableAccountByHandle ? call.getHandle().getScheme() : 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

该方法的做的事情很简单,新建一个CreateConnectionProcessor对象,并调用其process()方法

void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {    ......    mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,            phoneAccountRegistrar, mContext);    mCreateConnectionProcessor.process();}

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

将本通电话的PhoneAccountHandle添加到mAttemptRecords列表中,并调用attemptNextPhoneAccount()方法

public void process() {    Log.v(this, "process");    clearTimeout();    mAttemptRecords = new ArrayList<>();    // 将本通电话的PhoneAccountHandle信息添加到mAttemptRecords列表中    if (mCall.getTargetPhoneAccount() != null) {        mAttemptRecords.add(new CallAttemptRecord(                mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));    }    if (!mCall.isSelfManaged()) {        adjustAttemptsForConnectionManager();        adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());    }    mAttemptRecordIterator = mAttemptRecords.iterator();    attemptNextPhoneAccount();}

首先判断mAttemptRecords列表中的PhoneAccountHandle是否具有BIND_TELECOM_CONNECTION_SERVICE权限。

感觉这里就是对PhoneAccount做一次权限判断,来判定其是否能够绑定连接。

之后,调用ConnectionServiceRepository的getService()方法得到mService,而mService是一个ConnectionServiceWrapper类的实例对象

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);        // PhoneAccount进行权限判断OK后,将其获取        PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;        // 调用ConnectionServiceRepository的getService()方法得到mService对象        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);            // 调用mService对象的createConnection()方法            mService.createConnection(mCall, this);        }    } else {        Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");        DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?                mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);        notifyCallConnectionFailure(disconnectCause);    }}

我们可以把ConnectionServiceWrapper看做是一个代理类,因为其父类ServiceBinder是一个抽象类,且绑定了一个远程服务

查看ConnectionService.SERVICE_INTERFACE的定义,其就是"android.telecom.ConnectionService"

ConnectionServiceWrapper(        ComponentName componentName,        ConnectionServiceRepository connectionServiceRepository,        PhoneAccountRegistrar phoneAccountRegistrar,        CallsManager callsManager,        Context context,        TelecomSystem.SyncRoot lock,        UserHandle userHandle) {    // 调用父类的构造函数,而父类绑定了一个远程服务    super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);    mConnectionServiceRepository = connectionServiceRepository;    phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {        // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections        // To do this, we must proxy remote ConnectionService objects    });    mPhoneAccountRegistrar = phoneAccountRegistrar;    mCallsManager = callsManager;    mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);}// ConnectionServiceWrapper的父类是ServiceBinderprotected ServiceBinder(String serviceAction, ComponentName componentName, Context context,        TelecomSystem.SyncRoot lock, UserHandle userHandle) {    Preconditions.checkState(!TextUtils.isEmpty(serviceAction));    Preconditions.checkNotNull(componentName);    mContext = context;    mLock = lock;    mServiceAction = serviceAction;    mComponentName = componentName;    mUserHandle = userHandle;}

得知了mService是什么后,我们来看他的createConnection()方法

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

我们先看最后一句代码,其中的mBinder是ConnectionServiceWrapper的父类ServiceBinder的一个内部类Binder2的实例对象

在调用完bind()方法后,会根据成功与否回调callback对象的onSuccess()方法或者onFailure()方法

public void createConnection(final Call call, final CreateConnectionResponse response) {    Log.d(this, "createConnection(%s) via %s.", call, getComponentName());    BindCallback callback = new BindCallback() {        @Override        public void onSuccess() {            String callId = mCallIdMapper.getCallId(call);            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()));            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());         } 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.bind(callback, call);}

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

bind()方法中,在添加了回调之后,去绑定Service,当绑定成功后,会回调前面的onSuccess()方法

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();        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;            final boolean isBound;            if (mUserHandle != null) {                // 尝试绑定Service,并返回成功与否                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);            // 如果服务已经绑定过了,直接调用callback的onSuccess()方法            handleSuccessfulConnection();        }    }}

 

三、Telecom Framework与TeleService处理流程

在onSuccess()方法中,会调用mServiceInterface对象(即ConnectionService类中)的createConnection()方法,代码如下:

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

这里就是一个handleMessage的机制,去调用该类中另一个private的createConnection()方法

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()) {    @Override    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) {                        Log.d(this, "Enqueueing pre-init request %s", id);                        mPreInitializationConnectionRequests.add(                                new android.telecom.Logging.Runnable(                                        SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",                                        null /*lock*/) {                            @Override                            public void loggedRun() {                                createConnection(                                        connectionManagerPhoneAccount,                                        id,                                        request,                                        isIncoming,                                        isUnknown);                            }                        }.prepare());                    } else {                        createConnection(                                connectionManagerPhoneAccount,                                id,                                request,                                isIncoming,                                isUnknown);                    }                } finally {                    args.recycle();                    Log.endSession();                }                break;            }            ......            default:                break;        }    }};

在private的createConnection方法中,由于我们的的电话为MO Call,因此会调用onCreateOutgoingConnection()方法去创建connectiion对象

private void createConnection(        final PhoneAccountHandle callManagerAccount,        final String callId,        final ConnectionRequest request,        boolean isIncoming,        boolean isUnknown) {    Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +                    "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,            isIncoming,            isUnknown);    // 这里的connection是调用onCreateOutgoingConnection()方法创建的    Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)            : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)            : onCreateOutgoingConnection(callManagerAccount, request);    Log.d(this, "createConnection, connection: %s", connection);    ......}

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

这个方法比较长,但我们还是关注最后面部分的代码,调用了placeOutgoingConnection()方法

public Connection onCreateOutgoingConnection(        PhoneAccountHandle connectionManagerPhoneAccount,        final ConnectionRequest request) {    Log.i(this, "onCreateOutgoingConnection, request: " + request);    ......    // Convert into emergency number if necessary    // This is required in some regions (e.g. Taiwan).    if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number)) {    } else {        ......        // Get the right phone object from the account data passed in.        final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);        Connection resultConnection = getTelephonyConnection(request, numberToDial,                isEmergencyNumber, handle, phone);        // If there was a failure, the resulting connection will not be a TelephonyConnection,        // so don't place the call!        if (resultConnection instanceof TelephonyConnection) {            if (request.getExtras() != null && request.getExtras().getBoolean(                    TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {                ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);            }            placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);        }        return resultConnection;    }}

在这里,我们要关注的点是,调用了Phone对象的dial()方法,这也正式标志着,接下来,处理流程将进入Telephony Framework中

private void placeOutgoingConnection(        TelephonyConnection connection, Phone phone, ConnectionRequest request) {    placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());}private void placeOutgoingConnection(        TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {    String number = connection.getAddress().getSchemeSpecificPart();    com.android.internal.telephony.Connection originalConnection = null;    try {        if (phone != null) {            originalConnection = phone.dial(number, null, videoState, extras);        }    } catch (CallStateException e) {        Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);        int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;        if (e.getError() == CallStateException.ERROR_OUT_OF_SERVICE) {            cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;        } else if (e.getError() == CallStateException.ERROR_POWER_OFF) {            cause = android.telephony.DisconnectCause.POWER_OFF;        }        connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(                cause, e.getMessage()));        return;    }    if (originalConnection == null) {        ......    } else {        connection.setOriginalConnection(originalConnection);    }}

 

四、Telephony Framework处理通话流程

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

我们这里以GsmPhone为例,且不考虑VoLTE(即ImsPhone)的情况

调用dialInternal()方法

public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)        throws CallStateException {    ......    if (isPhoneTypeGsm()) {        // 我们这里以GsmPhone为例        return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);    } else {        return dialInternal(dialString, null, videoState, intentExtras);    }}

从dialInternal()方法中可以看到,接下来会去调用GsmCdmaCallTracker里面的dial()方法

protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,                                  Bundle intentExtras)        throws CallStateException {    return dialInternal(dialString, uusInfo, videoState, intentExtras, null);}protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,                                  Bundle intentExtras, ResultReceiver wrappedCallback)        throws CallStateException {    // Need to make sure dialString gets parsed properly    String newDialString = PhoneNumberUtils.stripSeparators(dialString);    if (isPhoneTypeGsm()) {        // handle in-call MMI first if applicable        if (handleInCallMmiCommands(newDialString)) {            return null;        }        // Only look at the Network portion for mmi        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);        GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this,                mUiccApplication.get(), wrappedCallback);        if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");        if (mmi == null) {            // 这里调用GsmCdmaCallTracker中的dial()方法            return mCT.dial(newDialString, uusInfo, intentExtras);        } else if (mmi.isTemporaryModeCLIR()) {            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);        } else {            mPendingMMIs.add(mmi);            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));            mmi.processCode();            return null;        }    } else {        return mCT.dial(newDialString);    }}

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

在GsmCdmaCallTracker的dial()方法中,会调用CommandsInterface的dial()方法,而RILJ实现了其dial()方法

public Connection dial(String dialString, UUSInfo uusInfo, Bundle intentExtras)        throws CallStateException {    return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);}public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,                                    Bundle intentExtras)        throws CallStateException {    // note that this triggers call state changed notif    clearDisconnected();    if (!canDial()) {        throw new CallStateException("cannot dial in current state");    }    String origNumber = dialString;    dialString = convertNumberIfNecessary(mPhone, dialString);    // The new call must be assigned to the foreground call.    // That call must be idle, so place anything that's    // there on hold    if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {        // this will probably be done by the radio anyway        // but the dial might fail before this happens        // and we need to make sure the foreground call is clear        // for the newly dialed connection        switchWaitingOrHoldingAndActive();        // This is a hack to delay DIAL so that it is sent out to RIL only after        // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to        // multi-way conference calls due to DIAL being sent out before SWITCH is processed        try {            Thread.sleep(500);        } catch (InterruptedException e) {            // do nothing        }        // Fake local state so that        // a) foregroundCall is empty for the newly dialed connection        // b) hasNonHangupStateChanged remains false in the        // next poll, so that we don't clear a failed dialing call        fakeHoldForegroundBeforeDial();    }    if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {        //we should have failed in !canDial() above before we get here        throw new CallStateException("cannot dial in current state");    }    boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),            dialString);    mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),            this, mForegroundCall, isEmergencyCall);    mHangupPendingMO = false;    mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);    if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0            || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {        // Phone number is invalid        mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;        // handlePollCalls() will notice this call not present        // and will mark it as dropped.        pollCallsWhenSafe();    } else {        // Always unmute when initiating a new call        setMute(false);        // 调用RILJ的dial()方法        mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());    }    if (mNumberConverted) {        mPendingMO.setConverted(origNumber);        mNumberConverted = false;    }    updatePhoneState();    mPhone.notifyPreciseCallStateChanged();    return mPendingMO;}

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

public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {    IRadio radioProxy = getRadioProxy(result);    if (radioProxy != null) {        RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,                mRILDefaultWorkSource);        Dial dialInfo = new Dial();        dialInfo.address = convertNullToEmptyString(address);        dialInfo.clir = clirMode;        if (uusInfo != null) {            UusInfo info = new UusInfo();            info.uusType = uusInfo.getType();            info.uusDcs = uusInfo.getDcs();            info.uusData = new String(uusInfo.getUserData());            dialInfo.uusInfo.add(info);        }        if (RILJ_LOGD) {            // Do not log function arg for privacy            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));        }        try {            // 这里之后,会经过一系列操作,将dial消息发送给C层的RIL代码,并最终传递到Modem            radioProxy.dial(rr.mSerial, dialInfo);        } catch (RemoteException | RuntimeException e) {            handleRadioProxyExceptionForRR(rr, "dial", e);        }    }}

到此为止,Dial的消息已经传递至RILJ,而后将继续把消息传递到C层以及Modem侧,并由Modem完成向网络发起呼叫的操作。

 

更多相关文章

  1. android graphics下的Paint类,Path类,Canvas类
  2. Android消息机制及HandlerThread、Handler内存泄漏问题
  3. Android事件传递机制(更加深入的了解事件的触发过程)
  4. android HTTP 通信, XML 解析, 通过 Hander 实现异步消息处理 (1)
  5. Android中Matrix的pre post set方法理解(来源:Linux社区 作者:zjmd
  6. Android(安卓)Accessibility使用及事件流程简介
  7. XML解析(一),SAX解析XML
  8. Android的Aidl安装方法
  9. android App全局SD卡路径统一管理

随机推荐

  1. 几道和「黑洞照片」那种海量数据有关的算
  2. 2020 年 Python 开发者调查报告:PyCharm
  3. 程序员用python给了女友一个七夕惊喜!
  4. GitHub 热门:实用 Python 编程
  5. 使用Nginx 代理应用服务的端口,以及ssh连
  6. LeetCode 上最难的链表算法题,没有之一!
  7. Stack Overflow 热帖:如何用 Python 调用
  8. 干货 | 滴滴 数据分析原来是这样做的!
  9. 几道 BAT 算法面试中经常问的「字符串」
  10. 11 月编程排行榜:Python “打败”Java 成