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 accounts; if (!isSelfManaged) { accounts = constructPossiblePhoneAccounts(handle, initiatingUser); Log.v(this, "startOutgoingCall found accounts = " + accounts); // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this // call as if a phoneAccount was not specified (does the default behavior instead). // Note: We will not attempt to dial with a requested phoneAccount if it is disabled. if (phoneAccountHandle != null) { if (!accounts.contains(phoneAccountHandle)) { phoneAccountHandle = null; } } if (phoneAccountHandle == null && accounts.size() > 0) { // No preset account, check if default exists that supports the URI scheme for the // handle and verify it can be used. if (accounts.size() > 1) { // 如果已注册的的PhoneAccountHandle数大于1 // 获取用于拨打电话的默认PhoneAccountHandle对象 PhoneAccountHandle defaultPhoneAccountHandle = mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme( handle.getScheme(), initiatingUser); // 获取到的PhoneAccountHandle对象不为null,且其包含在已注册列表中 if (defaultPhoneAccountHandle != null && accounts.contains(defaultPhoneAccountHandle)) { phoneAccountHandle = defaultPhoneAccountHandle; } } else { // 已注册的PhoneAccountHandle数为1,则选择该PhoneAccountHandle // Use the only PhoneAccount that is available phoneAccountHandle = accounts.get(0); } } } else { accounts = Collections.EMPTY_LIST; } // 使用获取到的PhoneAccountHandle对象,设置Call对象的PhoneAccount call.setTargetPhoneAccount(phoneAccountHandle); boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged; // Do not support any more live calls. Our options are to move a call to hold, disconnect // a call, or cancel this call altogether. If a call is being reused, then it has already // passed the makeRoomForOutgoingCall check once and will fail the second time due to the // call transitioning into the CONNECTING state. if (!isSelfManaged && !isPotentialInCallMMICode && (!isReusedCall && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) { ...... } boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 && !call.isEmergencyCall() && !isSelfManaged; if (needsAccountSelection) { // 如果前面选择PhoneAccountHandle的时候,已注册的的PhoneAccountHandle数大于1 // 但未设置拨打电话的默认PhoneAccountHandle // This is the state where the user is expected to select an account call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection"); // Create our own instance to modify (since extras may be Bundle.EMPTY) extras = new Bundle(extras); extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts); } else { call.setState( CallState.CONNECTING, phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString()); PhoneAccount accountToUse = mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser); if (extras != null && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { if (accountToUse != null && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { call.setRttStreams(true); } } } setIntentExtrasAndStartTime(call, extras); if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) { ...... } else if (!isSelfManaged && hasSelfManagedCalls() && !call.isEmergencyCall()) { ...... } else if (!mCalls.contains(call)) { // We check if mCalls already contains the call because we could potentially be reusing // a call which was previously added (See {@link #reuseOutgoingCall}). addCall(call); } return call;}
上面的代码中,有一个比较关键的constructPossiblePhoneAccounts()方法,我们来看一下他的实现
// Construct the list of possible PhoneAccounts that the outgoing call can use based on the// active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,// then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.private List constructPossiblePhoneAccounts(Uri handle, UserHandle user) { if (handle == null) { return Collections.emptyList(); } // 获取可以用于拨打电话的PhoneAccountHandle的列表 List allAccounts = mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user); // First check the Radio SIM Technology // 获取手机所支持的模式,单卡、双卡单通还是双卡双通等 if(mRadioSimVariants == null) { TelephonyManager tm = (TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE); // Cache Sim Variants mRadioSimVariants = tm.getMultiSimConfiguration(); } // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount // Should be available if a call is already active on the SIM account. if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) { List simAccounts = mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser(); PhoneAccountHandle ongoingCallAccount = null; for (Call c : mCalls) { if (!c.isDisconnected() && !c.isNew() && simAccounts.contains( c.getTargetPhoneAccount())) { ongoingCallAccount = c.getTargetPhoneAccount(); break; } } if (ongoingCallAccount != null) { // Remove all SIM accounts that are not the active SIM from the list. simAccounts.remove(ongoingCallAccount); allAccounts.removeAll(simAccounts); } } return allAccounts;}
在获取了PhoneAccountHandle的列表以后,我们回到刚刚startOutgoingCall()方法中。
当返回PhoneAccountHandle数大于一个时,则选择defaultPhoneAccountHandle来拨打电话;当返回PhoneAccountHandle数为一时,理所当然,只能选择这个PhoneAccountHandle。
在选好PhoneAccountHandle后,由于PhoneAccountHandle是PhoneAccount的唯一标识,因此我们也就得到了用于拨打电话的PhoneAccount。
此后,将Call对象的状态置为CONNECTING,我们的Call对象就建立完成了。
接下来,我们分析addCall()方法
private void addCall(Call call) { Trace.beginSection("addCall"); Log.v(this, "addCall(%s)", call); call.addListener(this); // 将新建的Call对象添加到mCalls列表中,以方便对所有Call对象进行管理 mCalls.add(call); // Specifies the time telecom finished routing the call. This is used by the dialer for // analytics. Bundle extras = call.getIntentExtras(); extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, SystemClock.elapsedRealtime()); updateCanAddCall(); // onCallAdded for calls which immediately take the foreground (like the first call). // 调用mListeners中所有listener的onCallAdded()方法 for (CallsManagerListener listener : mListeners) { if (LogUtils.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " addCall"); } listener.onCallAdded(call); if (LogUtils.SYSTRACE_DEBUG) { Trace.endSection(); } } Trace.endSection();}
在CallsManager的构造函数中,我们可以看到
mListeners.add(mInCallWakeLockController);mListeners.add(statusBarNotifier);mListeners.add(mCallLogManager);mListeners.add(mPhoneStateBroadcaster);mListeners.add(mInCallController);mListeners.add(mCallAudioManager);mListeners.add(missedCallNotifier);mListeners.add(mHeadsetMediaButton);mListeners.add(mProximitySensorManager);
这里就不展开讲每一个onCallAdded()方法具体做了些什么事情了,代码不难理解,大家感兴趣的话,可以自己看一看。
其中InCallController的onCallAdded()方法会通过一系列的步骤启动InCallUi,由于这里产生了分支,在同一篇文章中阐述容易造成混乱,因此我会在后续的文章中再详细介绍启动InCallUi的代码部分。
那么上面说了,processOutgoingCallIntent()方法做了两件事,第一步调用CallsManager的startOutgoingCall()方法建立Call对象并调用addCall()方法,这部分已经说完了。我们继续来说一说第二步,调用sendNewOutgoingCallIntent()方法。
/packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java
从代码中可以看出,我们建立了一个NewOutgoingCallIntentBroadcaster对象,并调用了其processIntent()方法
static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager, Intent intent) { // Asynchronous calls should not usually be made inside a BroadcastReceiver because once // onReceive is complete, the BroadcastReceiver's process runs the risk of getting // killed if memory is scarce. However, this is OK here because the entire Telecom // process will be running throughout the duration of the phone call and should never // be killed. final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false); NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster( context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(), isPrivilegedDialer); final int result = broadcaster.processIntent(); final boolean success = result == DisconnectCause.NOT_DISCONNECTED; if (!success && call != null) { disconnectCallAndShowErrorDialog(context, call, result); }}
/packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
我们这里只分析普通呼叫流程的代码,暂不讨论Voicemail和紧急呼叫的情况,请看代码中的中文注释
经过一系列判断,最后会调用broadcastIntent()方法
public int processIntent() { Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster."); Intent intent = mIntent; String action = intent.getAction(); final Uri handle = intent.getData(); if (handle == null) { Log.w(this, "Empty handle obtained from the call intent."); return DisconnectCause.INVALID_NUMBER; } // 正常呼叫流程下,该变量为false boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme()); if (isVoicemailNumber) { ...... } // 获取电话号码 String number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; } boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); if (!isUriNumber) { number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); number = mPhoneNumberUtilsAdapter.stripSeparators(number); } // 判断是否是潜在的紧急号码,此处为false final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number); Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber); rewriteCallIntentAction(intent, isPotentialEmergencyNumber); action = intent.getAction(); // True for certain types of numbers that are not intended to be intercepted or modified // by third parties (e.g. emergency numbers). boolean callImmediately = false; if (Intent.ACTION_CALL.equals(action)) { if (isPotentialEmergencyNumber) { ...... } } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { ...... } else { Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent); return DisconnectCause.INVALID_NUMBER; } // True for all managed calls, false for self-managed calls. boolean sendNewOutgoingCallBroadcast = true; PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra( TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); if (targetPhoneAccount != null) { PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked( targetPhoneAccount); // 前面提到过,第三方通话应用一般为SelfManaged的,而系统应用不是 if (phoneAccount != null && phoneAccount.isSelfManaged()) { callImmediately = true; sendNewOutgoingCallBroadcast = false; Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); } } // callImmediately为false if (callImmediately) { ...... } if (sendNewOutgoingCallBroadcast) { UserHandle targetUser = mCall.getInitiatingUser(); Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser); broadcastIntent(intent, number, !callImmediately, targetUser); } return DisconnectCause.NOT_DISCONNECTED;}
broadcastIntent()方法会调用Android系统的sendOrderedBroadcastAsUser()方法发送广播
由于调用时,第三个参数传递的值为true,因此,NewOutgoingCallBroadcastIntentReceiver类会在其onReceive()方法中对广播进行处理
private void broadcastIntent( Intent originalCallIntent, String number, boolean receiverRequired, UserHandle targetUser) { Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); if (number != null) { broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); } // Force receivers of this broadcast intent to run at foreground priority because we // want to finish processing the broadcast intent as soon as possible. broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); Log.v(this, "Broadcasting intent: %s.", broadcastIntent); checkAndCopyProviderExtras(originalCallIntent, broadcastIntent); mContext.sendOrderedBroadcastAsUser( broadcastIntent, targetUser, android.Manifest.permission.PROCESS_OUTGOING_CALLS, AppOpsManager.OP_PROCESS_OUTGOING_CALLS, receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null, null, // scheduler Activity.RESULT_OK, // initialCode number, // initialData: initial value for the result data (number to be modified) null); // initialExtras}
NewOutgoingCallBroadcastIntentReceiver是NewOutgoingCallIntentBroadcaster的子类,其onReceive()方法代码如下
在代码的最后,会调用CallsManager的placeOutgoingCall()方法
public void onReceive(Context context, Intent intent) { try { Log.startSession("NOCBIR.oR"); Trace.beginSection("onReceiveNewOutgoingCallBroadcast"); synchronized (mLock) { Log.v(this, "onReceive: %s", intent); // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is // used as the actual number to call. (If null, no call will be placed.) String resultNumber = getResultData(); Log.i(this, "Received new-outgoing-call-broadcast for %s with data %s", mCall, Log.pii(resultNumber)); ...... Uri resultHandleUri = Uri.fromParts( mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, resultNumber, null); ...... GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri); mCall.setNewOutgoingCallIntentBroadcastIsDone(); mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo, mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY)); } } finally { Trace.endSection(); Log.endSession(); }}
/packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
在对是否需要开启扬声器(Speaker)、是否为紧急呼叫以及PhoneAccount是否可用于拨打电话进行判断后,调用Call对象的startCreateConnection()方法
public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState) { if (call == null) { // don't do anything if the call no longer exists Log.i(this, "Canceling unknown call."); return; } final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); if (gatewayInfo == null) { Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); } else { Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", Log.pii(uriHandle), Log.pii(handle)); } call.setHandle(uriHandle); call.setGatewayInfo(gatewayInfo); // 这里判断是否要开启扬声器(Speaker),我们普通的电话默认是使用听筒的(Earpiece) final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean( R.bool.use_speaker_when_docked); final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock(); final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState); // Auto-enable speakerphone if the originating intent specified to do so, if the call // is a video call, of if using speaker when docked call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall || (useSpeakerWhenDocked && useSpeakerForDock)); call.setVideoState(videoState); if (speakerphoneOn) { Log.i(this, "%s Starting with speakerphone as requested", call); } else if (useSpeakerWhenDocked && useSpeakerForDock) { Log.i(this, "%s Starting with speakerphone because car is docked.", call); } else if (useSpeakerForVideoCall) { Log.i(this, "%s Starting with speakerphone because its a video call.", call); } // 不考虑紧急呼叫的情况 if (call.isEmergencyCall()) { new AsyncEmergencyContactNotifier(mContext).execute(); } final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean( com.android.internal.R.bool.config_requireCallCapableAccountForHandle); final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call, call.getTargetPhoneAccount()); // 进入这里 if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) { // If the account has been set, proceed to place the outgoing call. // Otherwise the connection will be initiated when the account is set by the user. if (call.isSelfManaged() && !isOutgoingCallPermitted) { notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call); } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) { markCallDisconnectedDueToSelfManagedCall(call); // 进入这里,调用Call对象的startCreateConnection()方法 } else { if (call.isEmergencyCall()) { // Disconnect all self-managed calls to make priority for emergency call. disconnectSelfManagedCalls(); } call.startCreateConnection(mPhoneAccountRegistrar); } } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts( requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false, call.getInitiatingUser()).isEmpty()) { // If there are no call capable accounts, disconnect the call. markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED, "No registered PhoneAccounts")); markCallAsRemoved(call); }}
/packages/services/Telecomm/src/com/android/server/telecom/Call.java
该方法的做的事情很简单,新建一个CreateConnectionProcessor对象,并调用其process()方法
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { ...... mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext); mCreateConnectionProcessor.process();}
/packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
将本通电话的PhoneAccountHandle添加到mAttemptRecords列表中,并调用attemptNextPhoneAccount()方法
public void process() { Log.v(this, "process"); clearTimeout(); mAttemptRecords = new ArrayList<>(); // 将本通电话的PhoneAccountHandle信息添加到mAttemptRecords列表中 if (mCall.getTargetPhoneAccount() != null) { mAttemptRecords.add(new CallAttemptRecord( mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount())); } if (!mCall.isSelfManaged()) { adjustAttemptsForConnectionManager(); adjustAttemptsForEmergency(mCall.getTargetPhoneAccount()); } mAttemptRecordIterator = mAttemptRecords.iterator(); attemptNextPhoneAccount();}
首先判断mAttemptRecords列表中的PhoneAccountHandle是否具有BIND_TELECOM_CONNECTION_SERVICE权限。
感觉这里就是对PhoneAccount做一次权限判断,来判定其是否能够绑定连接。
之后,调用ConnectionServiceRepository的getService()方法得到mService,而mService是一个ConnectionServiceWrapper类的实例对象
private void attemptNextPhoneAccount() { Log.v(this, "attemptNextPhoneAccount"); CallAttemptRecord attempt = null; if (mAttemptRecordIterator.hasNext()) { attempt = mAttemptRecordIterator.next(); if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( attempt.connectionManagerPhoneAccount)) { Log.w(this, "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for " + "attempt: %s", attempt); attemptNextPhoneAccount(); return; } // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it // also requires the BIND_TELECOM_CONNECTION_SERVICE permission. if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) && !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( attempt.targetPhoneAccount)) { Log.w(this, "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for " + "attempt: %s", attempt); attemptNextPhoneAccount(); return; } } if (mCallResponse != null && attempt != null) { Log.i(this, "Trying attempt %s", attempt); // PhoneAccount进行权限判断OK后,将其获取 PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount; // 调用ConnectionServiceRepository的getService()方法得到mService对象 mService = mRepository.getService(phoneAccount.getComponentName(), phoneAccount.getUserHandle()); if (mService == null) { Log.i(this, "Found no connection service for attempt %s", attempt); attemptNextPhoneAccount(); } else { mConnectionAttempt++; mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount); mCall.setTargetPhoneAccount(attempt.targetPhoneAccount); mCall.setConnectionService(mService); setTimeoutIfNeeded(mService, attempt); // 调用mService对象的createConnection()方法 mService.createConnection(mCall, this); } } else { Log.v(this, "attemptNextPhoneAccount, no more accounts, failing"); DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ? mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR); notifyCallConnectionFailure(disconnectCause); }}
我们可以把ConnectionServiceWrapper看做是一个代理类,因为其父类ServiceBinder是一个抽象类,且绑定了一个远程服务
查看ConnectionService.SERVICE_INTERFACE的定义,其就是"android.telecom.ConnectionService"
ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle) { // 调用父类的构造函数,而父类绑定了一个远程服务 super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle); mConnectionServiceRepository = connectionServiceRepository; phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() { // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections // To do this, we must proxy remote ConnectionService objects }); mPhoneAccountRegistrar = phoneAccountRegistrar; mCallsManager = callsManager; mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);}// ConnectionServiceWrapper的父类是ServiceBinderprotected ServiceBinder(String serviceAction, ComponentName componentName, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle) { Preconditions.checkState(!TextUtils.isEmpty(serviceAction)); Preconditions.checkNotNull(componentName); mContext = context; mLock = lock; mServiceAction = serviceAction; mComponentName = componentName; mUserHandle = userHandle;}
得知了mService是什么后,我们来看他的createConnection()方法
/packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
我们先看最后一句代码,其中的mBinder是ConnectionServiceWrapper的父类ServiceBinder的一个内部类Binder2的实例对象
在调用完bind()方法后,会根据成功与否回调callback对象的onSuccess()方法或者onFailure()方法
public void createConnection(final Call call, final CreateConnectionResponse response) { Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); BindCallback callback = new BindCallback() { @Override public void onSuccess() { String callId = mCallIdMapper.getCallId(call); mPendingResponses.put(callId, response); GatewayInfo gatewayInfo = call.getGatewayInfo(); Bundle extras = call.getIntentExtras(); if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && gatewayInfo.getOriginalAddress() != null) { extras = (Bundle) extras.clone(); extras.putString( TelecomManager.GATEWAY_PROVIDER_PACKAGE, gatewayInfo.getGatewayProviderPackageName()); extras.putParcelable( TelecomManager.GATEWAY_ORIGINAL_ADDRESS, gatewayInfo.getOriginalAddress()); } if (call.isIncoming() && mCallsManager.getEmergencyCallHelper() .getLastEmergencyCallTimeMillis() > 0) { // Add the last emergency call time to the connection request for incoming calls if (extras == call.getIntentExtras()) { extras = (Bundle) extras.clone(); } extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis()); } // Call is incoming and added because we're handing over from another; tell CS // that its expected to handover. if (call.isIncoming() && call.getHandoverSourceCall() != null) { extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, call.getHandoverSourceCall().getTargetPhoneAccount()); } Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.piiHandle(call.getHandle())); ConnectionRequest connectionRequest = new ConnectionRequest.Builder() .setAccountHandle(call.getTargetPhoneAccount()) .setAddress(call.getHandle()) .setExtras(extras) .setVideoState(call.getVideoState()) .setTelecomCallId(callId) // For self-managed incoming calls, if there is another ongoing call Telecom // is responsible for showing a UI to ask the user if they'd like to answer // this new incoming call. .setShouldShowIncomingCallUi( !mCallsManager.shouldShowSystemIncomingCallUi(call)) .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs()) .setRttPipeToInCall(call.getCsToInCallRttPipeForCs()) .build(); try { mServiceInterface.createConnection( call.getConnectionManagerPhoneAccount(), callId, connectionRequest, call.shouldAttachToExistingConnection(), call.isUnknown(), Log.getExternalSession()); } catch (RemoteException e) { Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); mPendingResponses.remove(callId).handleCreateConnectionFailure( new DisconnectCause(DisconnectCause.ERROR, e.toString())); } } @Override public void onFailure() { Log.e(this, new Exception(), "Failure to call %s", getComponentName()); response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); } }; mBinder.bind(callback, call);}
/packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java
bind()方法中,在添加了回调之后,去绑定Service,当绑定成功后,会回调前面的onSuccess()方法
final class Binder2 { /** * Performs an asynchronous bind to the service (only if not already bound) and executes the * specified callback. * * @param callback The callback to notify of the binding's success or failure. * @param call The call for which we are being bound. */ void bind(BindCallback callback, Call call) { Log.d(ServiceBinder.this, "bind()"); // Reset any abort request if we're asked to bind again. clearAbort(); if (!mCallbacks.isEmpty()) { // Binding already in progress, append to the list of callbacks and bail out. mCallbacks.add(callback); return; } // 添加回调 mCallbacks.add(callback); if (mServiceConnection == null) { Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName); ServiceConnection connection = new ServiceBinderConnection(call); Log.addEvent(call, LogUtils.Events.BIND_CS, mComponentName); final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; final boolean isBound; if (mUserHandle != null) { // 尝试绑定Service,并返回成功与否 isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags, mUserHandle); } else { isBound = mContext.bindService(serviceIntent, connection, bindingFlags); } if (!isBound) { handleFailedConnection(); return; } } else { Log.d(ServiceBinder.this, "Service is already bound."); Preconditions.checkNotNull(mBinder); // 如果服务已经绑定过了,直接调用callback的onSuccess()方法 handleSuccessfulConnection(); } }}
三、Telecom Framework与TeleService处理流程
在onSuccess()方法中,会调用mServiceInterface对象(即ConnectionService类中)的createConnection()方法,代码如下:
/frameworks/base/telecomm/java/android/telecom/ConnectionService.java
这里就是一个handleMessage的机制,去调用该类中另一个private的createConnection()方法
public void createConnection( PhoneAccountHandle connectionManagerPhoneAccount, String id, ConnectionRequest request, boolean isIncoming, boolean isUnknown, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_CREATE_CONN); try { SomeArgs args = SomeArgs.obtain(); args.arg1 = connectionManagerPhoneAccount; args.arg2 = id; args.arg3 = request; args.arg4 = Log.createSubsession(); args.argi1 = isIncoming ? 1 : 0; args.argi2 = isUnknown ? 1 : 0; mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget(); } finally { Log.endSession(); }}private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { ...... case MSG_CREATE_CONNECTION: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); try { final PhoneAccountHandle connectionManagerPhoneAccount = (PhoneAccountHandle) args.arg1; final String id = (String) args.arg2; final ConnectionRequest request = (ConnectionRequest) args.arg3; final boolean isIncoming = args.argi1 == 1; final boolean isUnknown = args.argi2 == 1; if (!mAreAccountsInitialized) { Log.d(this, "Enqueueing pre-init request %s", id); mPreInitializationConnectionRequests.add( new android.telecom.Logging.Runnable( SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR", null /*lock*/) { @Override public void loggedRun() { createConnection( connectionManagerPhoneAccount, id, request, isIncoming, isUnknown); } }.prepare()); } else { createConnection( connectionManagerPhoneAccount, id, request, isIncoming, isUnknown); } } finally { args.recycle(); Log.endSession(); } break; } ...... default: break; } }};
在private的createConnection方法中,由于我们的的电话为MO Call,因此会调用onCreateOutgoingConnection()方法去创建connectiion对象
private void createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown) { Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming, isUnknown); // 这里的connection是调用onCreateOutgoingConnection()方法创建的 Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) : onCreateOutgoingConnection(callManagerAccount, request); Log.d(this, "createConnection, connection: %s", connection); ......}
/packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
这个方法比较长,但我们还是关注最后面部分的代码,调用了placeOutgoingConnection()方法
public Connection onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, final ConnectionRequest request) { Log.i(this, "onCreateOutgoingConnection, request: " + request); ...... // Convert into emergency number if necessary // This is required in some regions (e.g. Taiwan). if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number)) { } else { ...... // Get the right phone object from the account data passed in. final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber); Connection resultConnection = getTelephonyConnection(request, numberToDial, isEmergencyNumber, handle, phone); // If there was a failure, the resulting connection will not be a TelephonyConnection, // so don't place the call! if (resultConnection instanceof TelephonyConnection) { if (request.getExtras() != null && request.getExtras().getBoolean( TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) { ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true); } placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request); } return resultConnection; }}
在这里,我们要关注的点是,调用了Phone对象的dial()方法,这也正式标志着,接下来,处理流程将进入Telephony Framework中
private void placeOutgoingConnection( TelephonyConnection connection, Phone phone, ConnectionRequest request) { placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());}private void placeOutgoingConnection( TelephonyConnection connection, Phone phone, int videoState, Bundle extras) { String number = connection.getAddress().getSchemeSpecificPart(); com.android.internal.telephony.Connection originalConnection = null; try { if (phone != null) { originalConnection = phone.dial(number, null, videoState, extras); } } catch (CallStateException e) { Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e); int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE; if (e.getError() == CallStateException.ERROR_OUT_OF_SERVICE) { cause = android.telephony.DisconnectCause.OUT_OF_SERVICE; } else if (e.getError() == CallStateException.ERROR_POWER_OFF) { cause = android.telephony.DisconnectCause.POWER_OFF; } connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( cause, e.getMessage())); return; } if (originalConnection == null) { ...... } else { connection.setOriginalConnection(originalConnection); }}
四、Telephony Framework处理通话流程
/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java
我们这里以GsmPhone为例,且不考虑VoLTE(即ImsPhone)的情况
调用dialInternal()方法
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) throws CallStateException { ...... if (isPhoneTypeGsm()) { // 我们这里以GsmPhone为例 return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras); } else { return dialInternal(dialString, null, videoState, intentExtras); }}
从dialInternal()方法中可以看到,接下来会去调用GsmCdmaCallTracker里面的dial()方法
protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) throws CallStateException { return dialInternal(dialString, uusInfo, videoState, intentExtras, null);}protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras, ResultReceiver wrappedCallback) throws CallStateException { // Need to make sure dialString gets parsed properly String newDialString = PhoneNumberUtils.stripSeparators(dialString); if (isPhoneTypeGsm()) { // handle in-call MMI first if applicable if (handleInCallMmiCommands(newDialString)) { return null; } // Only look at the Network portion for mmi String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get(), wrappedCallback); if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'..."); if (mmi == null) { // 这里调用GsmCdmaCallTracker中的dial()方法 return mCT.dial(newDialString, uusInfo, intentExtras); } else if (mmi.isTemporaryModeCLIR()) { return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras); } else { mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.processCode(); return null; } } else { return mCT.dial(newDialString); }}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
在GsmCdmaCallTracker的dial()方法中,会调用CommandsInterface的dial()方法,而RILJ实现了其dial()方法
public Connection dial(String dialString, UUSInfo uusInfo, Bundle intentExtras) throws CallStateException { return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);}public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras) throws CallStateException { // note that this triggers call state changed notif clearDisconnected(); if (!canDial()) { throw new CallStateException("cannot dial in current state"); } String origNumber = dialString; dialString = convertNumberIfNecessary(mPhone, dialString); // The new call must be assigned to the foreground call. // That call must be idle, so place anything that's // there on hold if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) { // this will probably be done by the radio anyway // but the dial might fail before this happens // and we need to make sure the foreground call is clear // for the newly dialed connection switchWaitingOrHoldingAndActive(); // This is a hack to delay DIAL so that it is sent out to RIL only after // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to // multi-way conference calls due to DIAL being sent out before SWITCH is processed try { Thread.sleep(500); } catch (InterruptedException e) { // do nothing } // Fake local state so that // a) foregroundCall is empty for the newly dialed connection // b) hasNonHangupStateChanged remains false in the // next poll, so that we don't clear a failed dialing call fakeHoldForegroundBeforeDial(); } if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) { //we should have failed in !canDial() above before we get here throw new CallStateException("cannot dial in current state"); } boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString); mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString), this, mForegroundCall, isEmergencyCall); mHangupPendingMO = false; mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo); if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { // Phone number is invalid mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; // handlePollCalls() will notice this call not present // and will mark it as dropped. pollCallsWhenSafe(); } else { // Always unmute when initiating a new call setMute(false); // 调用RILJ的dial()方法 mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage()); } if (mNumberConverted) { mPendingMO.setConverted(origNumber); mNumberConverted = false; } updatePhoneState(); mPhone.notifyPreciseCallStateChanged(); return mPendingMO;}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { IRadio radioProxy = getRadioProxy(result); if (radioProxy != null) { RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result, mRILDefaultWorkSource); Dial dialInfo = new Dial(); dialInfo.address = convertNullToEmptyString(address); dialInfo.clir = clirMode; if (uusInfo != null) { UusInfo info = new UusInfo(); info.uusType = uusInfo.getType(); info.uusDcs = uusInfo.getDcs(); info.uusData = new String(uusInfo.getUserData()); dialInfo.uusInfo.add(info); } if (RILJ_LOGD) { // Do not log function arg for privacy riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); } try { // 这里之后,会经过一系列操作,将dial消息发送给C层的RIL代码,并最终传递到Modem radioProxy.dial(rr.mSerial, dialInfo); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR(rr, "dial", e); } }}
到此为止,Dial的消息已经传递至RILJ,而后将继续把消息传递到C层以及Modem侧,并由Modem完成向网络发起呼叫的操作。
更多相关文章
- android graphics下的Paint类,Path类,Canvas类
- Android消息机制及HandlerThread、Handler内存泄漏问题
- Android事件传递机制(更加深入的了解事件的触发过程)
- android HTTP 通信, XML 解析, 通过 Hander 实现异步消息处理 (1)
- Android中Matrix的pre post set方法理解(来源:Linux社区 作者:zjmd
- Android(安卓)Accessibility使用及事件流程简介
- XML解析(一),SAX解析XML
- Android的Aidl安装方法
- android App全局SD卡路径统一管理