Android 7.0 去電流程
撥號的入口在TelecomManager的placeCall的方法,該方法實際呼叫TelecomServiceImpl的placeCall(),接著呼叫UserCallIntentProcessor的processIntent()方法
TelecomServiceImpl->placeCall()
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
try {
...
synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
if (extras != null) {
extras.setDefusable(true );
intent.putExtras(extras);
}
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, hasCallAppOp && hasCallPermission);//mUserCallIntentProcessorFactory是在TelecomSystem中初始化的
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}
UserCallIntentProcessor又在process(),UserCallIntentProcessor中方執行順序為
process()->processOutgoingCallIntent()->sendBroadcastToReceiver():
UserCallIntentProcessor->process():
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);
}
}
UserCallIntentProcessor->processOutgoingCallIntent()
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
...
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);
}
UserCallIntentProcessor->sendBroadcastToReceiver():
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;
}
UserCallIntentProcessor給PrimaryCallReceiver傳送廣播後, PrimaryCallReceiver在onReceive()實際上就了呼叫CallIntentProcessor的processIntent的方法,接著呼叫CallsManager的startOutgoingCall 初始化Call,然後NewOutgoingCallIntentBroadcaster的processIntent()方法:
public void onReceive(Context context, Intent intent) {
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
}
在CallIntentProcessor的processIntent方法中呼叫processOutgoingCallIntent()方法。
CallIntentProcessor->processOutgoingCallIntent():
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
...
Call call = callsManager
.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);//初始化call及啟動InCallUI
if (call != null) {
// 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.
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) {
callsManager.clearPendingMOEmergencyCall();
disconnectCallAndShowErrorDialog(context, call, result);
}
}
}
NewOutgoingCallIntentBroadcaster ->processIntent():
public int processIntent() {
...
if (callImmediately) {
boolean speakerphoneOn = mIntent.getBooleanExtra(
TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
int videoState = mIntent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
// Since we will not start NewOutgoingCallBroadcastIntentReceiver in case of
// callImmediately is true, make sure to mark it as ready, so that when user
// selects account, call can go ahead in case of numbers which are potential emergency
// but not actual emergeny.
mCall.setNewOutgoingCallIntentBroadcastIsDone();
mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
speakerphoneOn, videoState);
}
}
CallsManager->placeOutgoingCall():
...
if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
if (!call.isEmergencyCall()) {
updateLchStatus(call.getTargetPhoneAccount().getId());
}
call.startCreateConnection(mPhoneAccountRegistrar);
}
}
...
Call->startCreateConnection():
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
...
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
CreateConnectionProcessor->process():
public void process() {
clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
CreateConnectionProcessor->attemptNextPhoneAccount()
private void attemptNextPhoneAccount() {
...
if (mCallResponse != null && attempt != null) {
Log.i(this, "Trying attempt %s", attempt);
PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
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(mCall, this);
}
}
}
ConnectionServiceWrapper->createConnection():
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());
}
Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));
try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
callId,
new ConnectionRequest(
call.getTargetPhoneAccount(),
call.getHandle(),
extras,
call.getVideoState(),
callId),
call.shouldAttachToExistingConnection(),
call.isUnknown());
} 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);//回撥
}
ConnetctionService->createConnection():
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);
...
}
onCreateOutgoingConnection是在ConnetctionService的子類TelephonyConnectionService中實現的
TelephonyConnectionService->onCreateOutgoingConnection():
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
final ConnectionRequest request) {
...
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) {
placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
}
return resultConnection;
}
TelephonyConnectionService->placeOutgoingConnection():
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, int videoState, Bundle extras,
ConnectionRequest request) {
...
String number = connection.getAddress().getSchemeSpecificPart();
com.android.internal.telephony.Connection originalConnection = null;
try {
if (phone != null) {
if (isAddParticipant) {
phone.addParticipant(number);
return;
} else {
originalConnection = phone.dial(number, null, request.getVideoState(), bundle);
}
}
} catch (CallStateException e) {
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
if (e.getError() == CallStateException.ERROR_DISCONNECTED) {
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;
}
}
GsmCdmaPhone->dial():
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
throws CallStateException {
...
if (isPhoneTypeGsm()) {
return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
} else {
return dialInternal(dialString, null, videoState, intentExtras);
}
}
GsmCdmaPhone->dialInternal():
protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
Bundle intentExtras)
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());
if (DBG) logd("dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
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));
try {
mmi.processCode();
} catch (CallStateException e) {
//do nothing
}
// FIXME should this return null or something else?
return null;
}
} else {
return mCT.dial(newDialString);
}
}
GsmCdmaCallTracker->dial():
public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
Bundle intentExtras)
throws CallStateException {
...
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);
mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
}
if (mNumberConverted) {
mPendingMO.setConverted(origNumber);
mNumberConverted = false;
}
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
return mPendingMO;
}