转载请注明出处:https://blog.csdn.net/turtlejj/article/details/81240892,谢谢~
由于工作中需要熟悉Android拨打电话的完整流程,特将学习的过程记录下来,以便将来进行回顾,同时也欢迎大家对文章中不正确的地方加以指正。
在代码中,我在关键地方都添加了自己的对于代码理解的中文注释,已方便更好的理解代码的含义,以下就开始我们对拨号流程的梳理。
/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;
}
/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的父类是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();
}
}
}
在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);
}
}
/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完成向网络发起呼叫的操作。