MO Call 拨号流程分析

一、在拨号盘Dialer中点击拨号按钮
 
/packages/apps/Dialer/java/com/android/dialer/app/dialpad/DialpadFragment.java
按下拨号按钮后,会调用handleDialButtonPressed()方法

 1 public void onClick(View view) {
 2  int resId = view.getId();
 3  if (resId == R.id.dialpad_floating_action_button) {
 4   view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
 5   handleDialButtonPressed();
 6  } else if (resId == R.id.deleteButton) {
 7   keyPressed(KeyEvent.KEYCODE_DEL);
 8  } else if (resId == R.id.digits) {
 9   if (!isDigitsEmpty()) {
10 mDigits.setCursorVisible(true);
11   }
12  } else if (resId == R.id.dialpad_overflow) {
13   mOverflowPopupMenu.show();
14  } else {
15   LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);
16   return;
17  }
18 }

在handleDialButtonPressed()方法中,

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 {
// 正常流程
PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD));
   hideAndClearDialpad();
}
  }
}

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

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

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

 1 创建intent的具体流程如下
 2 public CallIntentBuilder(@NonNull String number, CallInitiationType.Type callInitiationType) {  // 调用CallUtil的getCallUri()方法,将号码封装成Uri
 3  this(CallUtil.getCallUri(Assert.isNotNull(number)), callInitiationType);
 4 }
 5 ->
 6  packages/apps/Dialer/java/com/android/dialer/util/CallUtil.java
 7 public static Uri getCallUri(String number) {
 8  if (PhoneNumberHelper.isUriNumber(number)) {  // 网络电话流程
 9 return Uri.fromParts(PhoneAccount.SCHEME_SIP, number, null);
10  }
11  // 普通电话流程
12  return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
13 }
14 
15 ->
16  
17 public CallIntentBuilder(@NonNull Uri uri, CallInitiationType.Type callInitiationType) {
18  // 调用CcreateCallSpecificAppData()方法,对callInitiationType进行转换
19  this(uri, createCallSpecificAppData(callInitiationType));
20 }
21  
22 ->
23 private static @NonNull CallSpecificAppData createCallSpecificAppData(
24 CallInitiationType.Type callInitiationType) {
25  CallSpecificAppData callSpecificAppData = CallSpecificAppData.newBuilder().setCallInitiationType(callInitiationType).build();
26  return callSpecificAppData;
27 }
28  
29 ->
30 
31 public Intent build() {
32  // 设置intent的action为ACTION_CALL
33  Intent intent = new Intent(Intent.ACTION_CALL, uri);
34  // 普通电话为VideoProfile.STATE_AUDIO_ONLY
35  intent.putExtra(
36   TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
37   isVideoCall  VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);
38  Bundle extras = new Bundle();
39  extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
40  CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData);
41  intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
42  // 由于没有设置PhoneAccountHandle,因此为null
43  if (phoneAccountHandle != null) {
44 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
45  }
46  
47  if (!TextUtils.isEmpty(callSubject)) {
48 intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
49  }
50  return intent;
51 }


/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

1 public static boolean placeCall(Context context, Intent intent) {
2       if (hasCallPhonePermission(context)) {
3         getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
4        return true;
5       }
6      return false;
7     }
1 private static TelecomManager getTelecomManager(Context context)
2 {
3        return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
4    }

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

二、 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);
  }
 }
}

private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
              return mTelecomServiceOverride;
          }
         return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}

frameworks/base/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
   void placeCall(in Uri handle, in Bundle extras, String callingPackage);


/packages/servies/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
->
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 的processIntent()方法
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);
 }
}

/packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
调用processOutgoingCallIntent()方法

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);
 sendIntentToDestination(intent, isLocalInvocation);
// sendBroadcastToReceiver(intent);
}
调用sendBroadcastToReceiver()方法

设置广播接收者为PrimaryCallReveiver.class,并发送广播
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
         intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setClass(mContext, PrimaryCallReceiver.class);
         if (isLocalInvocation) {
             // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
             // bother trampolining the intent, just sent it directly to the call intent processor.
             // TODO: We should not be using an intent here; this whole flows needs cleanup.
             Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
             synchronized (TelecomSystem.getInstance().getLock()) {
                 TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
             }
          } else {
             // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
             // process; we need to trampoline to TelecomSystem in the system server process.
             Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
             mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
         }
         return true;
     }

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
public TelecomSystem getTelecomSystem() {
         return TelecomSystem.getInstance();
    }
调用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系统会为每一张Sim卡建立一个PhoneAccount,也会为网络电话建立PhoneAccount,在手机不插卡时,还会建立一个用于拨打紧急呼叫的PhoneAccount。而每一个PhoneAccount都含有一个PhoneAccountHandle,PhoneAccountHandle是每一个PhoneAccount的唯一标识。

显而易见,最常见的情况下,defaultPhoneAccountHandle就是手机在插入两张Sim卡时,系统默认用于拨打电话的那一个PhoneAccountHandle。
有了以上的概念,我们来大致看一下Call对象是如何创建的.
/packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
public 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);

// isSelfManaged  false
boolean isSelfManaged = account != null && account.isSelfManaged();
// 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 = 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 */
                     mClockProxy);
             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.call.setIsSelfManaged(isSelfManaged);  
  // 第三方通话应用一般是SelfManaged的,我们这里只关心Android系统的Dialer,所以该值为false
 if (isSelfManaged) {
Self-managed calls will ALWAYS use voip audio mode.
            call.setIsVoipAudioMode(true);
         }
         call.setInitiatingUser(initiatingUser);
         isReusedCall = false;
      }
     int videoState = VideoProfile.STATE_AUDIO_ONLY;
     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 = 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)) {
    if (call.isEmergencyCall() && account != null &&
                         !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
Phone account doesn't support emergency video calling, so fallback to
 audio-only now to prevent the InCall UI from setting up video surfaces
needlessly.
Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
                          "falling back to audio-only");
                     videoState = VideoProfile.STATE_AUDIO_ONLY;
               } else if (account != null &&
                         !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
   Phone account doesn't support video calling, so fallback to audio-only.
 Log.i(this, "startOutgoingCall - video calls not supported; fallback to " +
                           "audio-only.");
           videoState = VideoProfile.STATE_AUDIO_ONLY;
       }
      }
             call.setVideoState(videoState);
          }
         List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
                 phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser);
         if (potentialPhoneAccounts.size() == 1) {  //单卡// 已注册的PhoneAccountHandle数为1,则选择该PhoneAccountHandle
              phoneAccountHandle = potentialPhoneAccounts.get(0);
      } else { //双卡
              phoneAccountHandle = null;}
// 使用获取到的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 (!isPotentialInCallMMICode && (!isReusedCall
                && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
    Call foregroundCall = getForegroundCall();
    Log.d(this, "No more room for outgoing call %s ", call);
if (foregroundCall.isSelfManaged()) {
// If the ongoing call is a self-managed call, then prompt the user to ask if they'd like to disconnect their ongoing call and place the outgoing call.
  call.setOriginalCallIntent(originalIntent);
              startCallConfirmation(call);
       } else {
                // If the ongoing call is a managed call, we will prevent the outgoing call from
                // dialing.
    notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
     }
      return null;
    }
// The outgoing call can be placed, go forward.
 boolean needsAccountSelection =
                  phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
          && !call.isEmergencyCall() && !isSelfManaged;
         if (needsAccountSelection) {  //双卡情形,// 如果前面选择PhoneAccountHandle的时候,已注册的的PhoneAccountHandle数大于1
  // 但未设置拨打电话的默认PhoneAccountHandle
 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,
               potentialPhoneAccounts);
         } else {   //单卡情形
PhoneAccount accountToUse =                     mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
if (accountToUse != null && accountToUse.getExtras() != null) {
   if (accountToUse.getExtras()
    .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
   Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
                     call.getId());
                     call.setIsVoipAudioMode(true);
                 }
              }
 
call.setState(CallState.CONNECTING,
phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
if (isRttSettingOn() || (extras != null && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false))) {
  Log.d(this, "Outgoing call requesting RTT, rtt setting is %b", isRttSettingOn());
  if (accountToUse != null && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                     call.createRttStreams();}
// Even if the phone account doesn't support RTT yet, the connection manager might
// change that. Set this to check it later.
call.setRequestedToStartWithRtt();
            }
         }
         setIntentExtrasAndStartTime(call, extras);

        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
             // Do not add the call if it is a potential MMI code.
            call.addListener(this);
         } 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;
     }

public List<PhoneAccountHandle> findOutgoingCallPhoneAccount(
            PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,UserHandle initiatingUser) {
boolean isSelfManaged = isSelfManaged(targetPhoneAccountHandle, initiatingUser); ///false
     List<PhoneAccountHandle> accounts;
  if (!isSelfManaged) {
          // Try to find a potential phone account, taking into account whether this is a video call.
accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo);
if (isVideo && accounts.size() == 0) {
// Placing a video call but no video capable accounts were found, so consider any call capable accounts (we can fallback to audio).
accounts = constructPossiblePhoneAccounts(handle, initiatingUser,false /* isVideo */);
}
Log.v(this, "findOutgoingCallPhoneAccount: 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.
// targetPhoneAccountHandle=null
if (targetPhoneAccountHandle != null) {
if (!accounts.contains(targetPhoneAccountHandle)) {
targetPhoneAccountHandle = null;
} else {
// The target phone account is valid and was found.
return Arrays.asList(targetPhoneAccountHandle);
}
}
if (targetPhoneAccountHandle == 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 defaultPhoneAccountHandle =                         mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
   handle.getScheme(), initiatingUser);
     if (defaultPhoneAccountHandle != null &accounts.contains(defaultPhoneAccountHandle)) {
accounts.clear();
accounts.add(defaultPhoneAccountHandle);
}
}
}
} else {
// Self-managed ConnectionServices can only have a single potential account.
    accounts = Arrays.asList(targetPhoneAccountHandle);
}
return accounts;
}

上面的代码中,有一个比较关键的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></phoneaccounthandle></phoneaccounthandle>
在获取了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) { //voicemail
  ......
 }
 
 // 获取电话号码
 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);
placeOutgoingCallImmediately(mCall, resultHandleUri, gatewayInfo,
                            mIntent.getBooleanExtra(
                                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false),
                            mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                                   VideoProfile.STATE_AUDIO_ONLY));
                 }
           } finally {
                 Trace.endSection();
                Log.endSession();
             }
          }
     }

private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,boolean speakerphoneOn, int videoState) {
Log.i(this,"Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
// Since we are not going to go through "Outgoing call broadcast", make sure// we mark it as ready.
mCall.setNewOutgoingCallIntentBroadcastIsDone();
mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
}
/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),我们普通的电话默认是使用听筒的(Handset)
 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列表中的PhoneAccountRegistrar是否具有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();
  }
 }
}
frameworks/base/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
void createConnection(
              in PhoneAccountHandle connectionManagerPhoneAccount,
              String callId,
              in ConnectionRequest request,
              boolean isIncoming,
              boolean isUnknown,        
      in Session.Info sessionInfo);

三、    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_ADD_CONNECTION_SERVICE_ADAPTER: {
                   …//省略
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;
}
protected void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
boolean isLegacyHandover = request.getExtras() != null &&
               request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
"isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
isHandover);
Connection connection = null;
if (isHandover) {
PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
? (PhoneAccountHandle) request.getExtras().getParcelable(
TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
if (!isIncoming) {
connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
} else {
connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
     }
  } else {
connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);
}
Log.d(this, "createConnection, connection: %s", connection);
if (connection == null) {
Log.i(this, "createConnection, implementation returned null connection.");
connection = Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
}
connection.setTelecomCallId(callId);
if (connection.getState() != Connection.STATE_DISCONNECTED) {
addConnection(request.getAccountHandle(), callId, connection);
  }
     Uri address = connection.getAddress();
     String number = address == null ? "null" : address.getSchemeSpecificPart();
         Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
                 Connection.toLogSafePhoneNumber(number),
                Connection.stateToString(connection.getState()),
                Connection.capabilitiesToString(connection.getConnectionCapabilities()),
                 Connection.propertiesToString(connection.getConnectionProperties()));
        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
        mAdapter.handleCreateConnectionComplete(
               callId,request,
new ParcelableConnection(
request.getAccountHandle(),
connection.getState(),
connection.getConnectionCapabilities(),
connection.getConnectionProperties(),
connection.getSupportedAudioRoutes(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
connection.getCallerDisplayNamePresentation(),
connection.getVideoProvider() == null ?
null : connection.getVideoProvider().getInterface(),
connection.getVideoState(),
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getConnectTimeMillis(),
connection.getConnectElapsedTimeMillis(),
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),connection.getExtras()));  
if (isIncoming && request.shouldShowIncomingCallUi() &&
 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
Connection.PROPERTY_SELF_MANAGED) {
// Tell ConnectionService to show its incoming call UX.
  connection.onShowIncomingCallUi();
}
if (isUnknown) {
   triggerConferenceRecalculate();
   }
    }

在private的createConnection方法中,由于我们的的电话为MO Call,因此会调用onCreateOutgoingConnection()方法去创建connectiion对象。
/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) {
String number = connection.getAddress().getSchemeSpecificPart();
boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number);
placeOutgoingConnection(connection, phone, request, isEmergencyNumber);
}

private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request,
boolean isEmergencyNumber) {
placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras(),isEmergencyNumber);}

protected void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, int videoState, Bundle extras,
boolean isEmergencyNumber) {
String number = connection.getAddress().getSchemeSpecificPart();
/// M: CC: Set PhoneAccountHandle for ECC @{
//[ALPS01794357]
if (isEmergencyNumber) {
final PhoneAccountHandle phoneAccountHandle = makePstnPhoneAccountHandleForEcc(phone);
log("placeOutgoingConnection, set back account mId: " +phoneAccountHandle.getId());
connection.setPhoneAccountHandle(phoneAccountHandle);
// Need to set current ECC phone type which will be used when retry to check if need to switch phone or not.            MtkTelephonyConnectionServiceUtil.getInstance().setEccPhoneType(phone.getPhoneType());
}
@}
com.android.internal.telephony.Connection originalConnection = null;
try {
if (phone != null) {
/// M: put MO from-line-number for Digits @{
Context context = getApplicationContext();
extras = ExtensionManager.getDigitsUtilExt().putLineNumberToExtras(extras, context);
@}
originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
                      .setVideoState(videoState)
                     .setIntentExtras(extras)
                        .setRttTextStream(connection.getRttTextStream())
                       .build());
             }
       } 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;
             }
           /// M: CC: ECC retry @{
            // Assume only one ECC exists
             if (MtkTelephonyConnectionServiceUtil.getInstance().isEccRetryOn()) {
                Log.i(this, "ECC retry: clear ECC param");
                 MtkTelephonyConnectionServiceUtil.getInstance().clearEccRetryParams();
             }
            /// @}
             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                      cause, e.getMessage()));
             /// M: CC: to check whether the device has on-going ECC
             MtkTelephonyConnectionServiceUtil.getInstance().setInEcc(false);
             connection.clearOriginalConnection();
             connection.destroy();
             return;
         }
         if (originalConnection == null) {
             int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
             if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
                 Log.d(this, "dialed MMI code");
                  int subId = phone.getSubId();
                 Log.d(this, "subId: "+subId);
                  telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
                  final Intent intent = new Intent(this, MMIDialogActivity.class);
                  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                          Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                  if (SubscriptionManager.isValidSubscriptionId(subId)) {
                     intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
                  }
                startActivity(intent);
             }
              Log.d(this, "placeOutgoingConnection, phone.dial returned null");
if (MtkTelephonyConnectionServiceUtil.getInstance().isEccRetryOn()) {
     Log.i(this, "ECC retry: clear ECC param");
                MtkTelephonyConnectionServiceUtil.getInstance().clearEccRetryParams();
              }
         @}
              connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                  telephonyDisconnectCause, "Connection is null"));
             /// M: CC: to check whether the device has on-going ECC
            MtkTelephonyConnectionServiceUtil.getInstance().setInEcc(false);
              connection.clearOriginalConnection();
             connection.destroy();
          } 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, @NonNull DialArgs dialArgs)
throws CallStateException {
if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
  // 我们这里以GsmPhone为例
throw new CallStateException("Sending UUS information NOT supported in CDMA!");
}
boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
Phone imsPhone = mImsPhone;
CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
.getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
boolean useImsForCall = isImsUseEnabled()&& imsPhone != null
&& (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
 (imsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
&& (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
boolean useImsForEmergency = imsPhone != null
&& isEmergency
&& alwaysTryImsForEmergencyCarrierConfig
& ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
&& imsPhone.isImsAvailable();
String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
stripSeparators(dialString));
boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#"))
&& dialPart.endsWith("#");
boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
        if (DBG) {
           logd("useImsForCall=" + useImsForCall
                   + ", useImsForEmergency=" + useImsForEmergency
                     + ", useImsForUt=" + useImsForUt
             + ", isUt=" + isUt
                  + ", imsPhone=" + imsPhone
                  + ", imsPhone.isVolteEnabled()="
                  + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
                     + ", imsPhone.isVowifiEnabled()="
                     + ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A")
                      + ", imsPhone.isVideoEnabled()="
                    + ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A")
                   + ", imsPhone.getServiceState().getState()="
                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
      }

        Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);

       if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
           try {
                 if (DBG) logd("Trying IMS PS call");
             return imsPhone.dial(dialString, dialArgs);
            } catch (CallStateException e) {
                if (DBG) logd("IMS PS call exception " + e +
                  "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
Do not throw a CallStateException and instead fall back to Circuit switch
// for emergency calls and MMI codes.
if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
   + "to CS.");
} else {
CallStateException ce = new CallStateException(e.getMessage());
ce.setStackTrace(e.getStackTrace());
             throw ce;
                }
           }
         }
  if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
               && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
    throw new CallStateException("cannot dial in current state");
         }
      // Check non-emergency voice CS call - shouldn't dial when POWER_OFF
     if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
     && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
  && !isEmergency /* non-emergency call */) {
throw new CallStateException(
  CallStateException.ERROR_POWER_OFF,
   "cannot dial voice call in airplane mode");
}
// Check for service before placing non emergency CS voice call.
// Allow dial only if either CS is camped on any RAT (or) PS is in LTE service.
if (mSST != null
&& mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */
   && !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
    && ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */
  && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
  && !isEmergency /* non-emergency call */) {
throw new CallStateException(
.ERROR_OUT_OF_SERVICE,
"cannot dial voice call in out of service");
}
if (DBG) logd("Trying (non-IMS) CS call");
if (isPhoneTypeGsm()) {
        return dialInternal(dialString, new DialArgs.Builder<>()
                   .setIntentExtras(dialArgs.intentExtras)
                     .build());
} else {
      return dialInternal(dialString, dialArgs);
          }
   }
frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java
protected Connection dialInternal(String dialString, DialArgs dialArgs)
             throws CallStateException {
       return dialInternal(dialString, dialArgs, null);
   }

/vendor/mediatek/proprietary/frameworks/opt/telephony/src/java/com/mediatek/internal/telephony/MtkGsmCdmaPhone.java

protected Connection dialInternal(String dialString, DialArgs dialArgs,
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);
MtkGsmMmiCode mmi = MtkGsmMmiCode.newFromDialString(networkPortion, this,
mUiccApplication.get(), wrappedCallback);
if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
// 这里调用GsmCdmaCallTracker中的dial()方法

return mCT.dial(newDialString, dialArgs.uusInfo, dialArgs.intentExtras);
  } else if (mmi.isTemporaryModeCLIR()) {
return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,
dialArgs.intentExtras);
             } else {
                 mPendingMMIs.add(mmi);
                 /* Print MMI content */
              Rlog.d(LOG_TAG, "dialInternal: " + dialString + ", mmi=" + mmi);
  dumpPendingMmi();
   mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
   mmi.processCode();
  return null;
  }
} else {
return mCT.dial(newDialString);
}
}
接下来会去调用vendor/mediatek/proprietary/frameworks/opt/telephony/src/java/com/mediatek/internal/telephony/MtkGsmCdmaCallTracker.java
的dial()方法中,
//GSM
/**
* clirMode is one of the CLIR_ constants*/
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");
        }// M: CC: For 93, MD can switch phoneType when SIM not inserted,
  // TeleService won't trigger phone switch, so check both SIM's ECC
//boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
 /        dialString);
        boolean isEmergencyCall = false;
         boolean isEcc = false;
if (hasC2kOverImsModem() &&
       (!TelephonyManager.getDefault().hasIccCard(mPhone.getPhoneId())
|| mPhone.getServiceState().getState() != ServiceState.STATE_IN_SERVICE)) {
isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(
                     mPhone.getContext(), dialString);
isEcc = PhoneNumberUtils.isEmergencyNumber(dialString);
     } else {
isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(
                  mPhone.getContext(), mPhone.getSubId(), dialString);
isEcc = PhoneNumberUtils.isEmergencyNumber(mPhone.getSubId(), dialString);
       }
mPendingMO = new MtkGsmCdmaConnection(mPhone,checkForTestEmergencyNumber(dialString),
                  this, mForegroundCall, isEmergencyCall);
          mHangupPendingMO = false;
mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);
  /// M: CC: Reconstruct dialString based on extras @{
 String newDialString = mMtkGsmCdmaCallTrackerExt.convertDialString(
            intentExtras, mPendingMO.getAddress());
    if (newDialString != null) {
          mPendingMO.setConnectionExtras(intentExtras);
       }
/// @}
if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
    || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
// Phone number is invalid
   mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
/// M: CC: Proprietary CRSS handling @{
mWaitForHoldToRedialRequest.resetToRedial();
/// @}
// handlePollCalls() will notice this call not present and will mark it as dropped.
pollCallsWhenSafe();
} else {
   // Always unmute when initiating a new call
              setMute(false);
/// M: CC: Proprietary CRSS handling @{
if (!mWaitForHoldToRedialRequest.isWaitToRedial()) {
/// M: CC: Proprietary ECC handling@{
if (isEcc && !MtkPhoneNumberUtils.isSpecialEmergencyNumber(
  mPhone.getSubId(), dialString)) {
int serviceCategory = MtkPhoneNumberUtils.getServiceCategoryFromEccBySubId(
dialString, mPhone.getSubId());
mMtkCi.setEccServiceCategory(serviceCategory, null);
       mMtkCi.emergencyDial(mPendingMO.getAddress(), clirMode, uusInfo,
                 obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));
   /// @}
             } else {
                   /// M: CC: If dialString is reconstructed, need to mark it as converted
if (newDialString != null) {
mNumberConverted = true;
} else {
newDialString = mPendingMO.getAddress();
         }
 // 调用RILJ的dial()方法
mCi.dial(newDialString, clirMode, uusInfo, obtainCompleteMessage());
              }
} else {
/// M: CC: If dialString is reconstructed, need to mark it as converted
if (newDialString != null) {
mNumberConverted = true;
                  } else {
                     newDialString = mPendingMO.getAddress();
                  }
mWaitForHoldToRedialRequest.setToRedial(newDialString, clirMode, uusInfo);
            }
        /// @}
      }
        if (mNumberConverted) {
             mPendingMO.setConverted(origNumber);
             mNumberConverted = false;
         }
       updatePhoneState();
       mPhone.notifyPreciseCallStateChanged();         
return mPendingMO;
     }
/frameworks/opt/telephony/src/java/com/android/internal/telephony/CallTracker.java
public CommandsInterface mCi;
void dial (String address, int clirMode, Message result);
void dial(String address, int clirMode, UUSInfo uusInfo, Message result);
frameworks/opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java
会调用CommandsInterface的dial()方法,而RILJ实现了其dial()方法,


/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完成向网络发起呼叫的操作。
 
 
五、    Call 流程总结
5.1 进程
Android L之后的版本对Call的流程进行了更加细致的划分,Telephony_Phone模块的架构进行了调整,调整之后的Telephony_Phone各个模块之间耦合性更低,分工也更为明确,但整个流程却变复杂了许多。
现在整个Telephony Phone模块包括了5个部分:InCallUI、Telecom Service、Telecom Framework、Telephony Service、Telephony Framework。
Telephony Phone的工作流程主要包含:
com.android.incallui(InCallUI)、
com.android.dialer(Dialer)、
com.android.server.telecom(Telecom Service)、
com.android.phone(Telephony Service) 四个进程
5.2  概要图
 
图1 呼叫流程图
 MO流程分为三个步骤,即Dial,Start InCallUI,Update Call State,即上图的三条不同颜色先说标识的三个流程。
从上图中可以看到,无论是InCallUI、Telecom Service还是Telephony Service均没有直接交互,所有的交互都是与Framework进行。最重要的一点是:InCallUI不再与Telephony Service或者Telecom Service绑定,各个模块非常独立。

猜你喜欢

转载自www.cnblogs.com/yz123/p/11984369.html
今日推荐