Android 8.0 撥號流程分析

轉載請註明出處: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<PhoneAccountHandle> 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<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user) {
    if (handle == null) {
        return Collections.emptyList();
    }
    // 獲取可以用於撥打電話的PhoneAccountHandle的列表
    List<PhoneAccountHandle> 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<PhoneAccountHandle> 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的父類是ServiceBinder
protected 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完成向網絡發起呼叫的操作。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章