拨打电话一般都是用户通过在Dialer中输入号码,然后点击拨打按钮开始的,下面我们就来一步步分析下,Android M上MO的具体流程
1、用户在电话应用中输入号码,然后拨打,在Dialer中处理拨打按钮事件的方法如下:
packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java
private void handleDialButtonPressed() {
if (isDigitsEmpty()) { // No number entered.
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)) {
Log.i(TAG, "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 = IntentUtil.getCallIntent(number,
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null));
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
hideAndClearDialpad(false);
}
}
}
2、在DialerUtils.java中 startActivityWithErrorToast方法,调用
android.
telecom.
TelecomManager中的
placeCall
packages/apps/Dialer/src/com/android/dialer/util/DialerUtils.java
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
try {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {
// All dialer-initiated calls should pass the touch point to the InCallUI
Point touchPoint = TouchPointManager.getInstance().getPoint();
if (touchPoint.x != 0 || touchPoint.y != 0) {
Bundle extras = new Bundle();
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
3、在TelecomManager 中
ITelecomService的placeCall的方法
frameworks/base/telecomm/java/android/telecom/TelecomManager.java
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);
}
}
}
4、ITelecomService是一个AIDL文件,TelecomServiceImpl.java是这个aidl文件的实现类
packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
enforceCallingPackage(callingPackage);
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 {
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
intent.putExtras(extras);
new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
callingPackage, hasCallAppOp && hasCallPermission);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
5、在上面placeCall里创建UserCallIntentProcessor.java类的实例,然后调用processIntent方法
packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
// Ensure call intents are not processed on devices that are not capable of calling.
if (!isVoiceCapable()) {
return;
}
String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
}
}
packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
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.OWNER);
return true;
}
7、在PrimaryCallReceiver.java中的onReceive方法通过 getTelecomSystem().getCallIntentProcessor().processIntent(intent)调用CallIntentProcessor.java里的processIntent方法,通过call类型的不同,调用processUnknownCallIntent或processOutgoingCallIntent
packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java
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();
}
8、在CallIntentProcessor的processOutgoingCallIntent中会通过callsManager.startOutgoingCall启动InCallUI,创建NewOutgoingCallIntentBroadcaster的实例broadcaster,并且调用processIntent方法
packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
.....
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
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, 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
private void broadcastIntent(
Intent originalCallIntent,
String number,
boolean receiverRequired) {
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);
Log.v(this, "Broadcasting intent: %s.", broadcastIntent);
checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);
mContext.sendOrderedBroadcastAsUser(
broadcastIntent,
UserHandle.CURRENT,
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
}
10、在NewOutgoingCallBroadcastIntentReceiver的onReciver中调用 CallsManager的placeOutgoingCall方法,然后调用 Call的startCreateConnection发起拨打电话的连接
packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
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);
call.setVideoState(videoState);
if (speakerphoneOn) {
Log.i(this, "%s Starting with speakerphone as requested", call);
} else {
Log.i(this, "%s Starting with speakerphone because car is docked.", call);
}
call.setStartWithSpeakerphoneOn(speakerphoneOn || mDockManager.isDocked());
if (call.isEmergencyCall()) {
// Emergency -- CreateConnectionProcessor will choose accounts automatically
call.setTargetPhoneAccount(null);
}
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.
call.startCreateConnection(mPhoneAccountRegistrar);
}
}
packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
/**
* Creates a new connection for a new outgoing call or to attach to an existing incoming call.
*/
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()),
call.isIncoming(),
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);
}
最后通过mServiceInterface.createConnection来创建连接,这里需要注意的是mServiceInterface为IConnectionService的实例,在ServiceBinder中通过setComponent(mComponentName)指定TelephonyConnectionService来实现这个方法,mComponentName在PhoneAccountHandle里初始化的
12、在TelephonyConnectionService的父类ConnectionService的createConnection中调用了onCreateOutgoingConnection,并在这个方法中调用placeOutgoingConnection,来调用phone 的dial方法
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request) {
String number = connection.getAddress().getSchemeSpecificPart();
com.android.internal.telephony.Connection originalConnection;
try {
originalConnection =
phone.dial(number, null, request.getVideoState(), request.getExtras());
} 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;
}
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
cause, e.getMessage()));
return;
}
if (originalConnection == null) {
int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
Log.d(this, "dialed MMI code");
telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
final Intent intent = new Intent(this, MMIDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
Log.d(this, "placeOutgoingConnection, phone.dial returned null");
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
telephonyDisconnectCause, "Connection is null"));
} else {
connection.setOriginalConnection(originalConnection);
}
}
13、通过phone对象的实例GsmPhone.dial调用dialInternal,来调用GsmCallTracker的dial方法
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java
@Override
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);
// 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 (LOCAL_DEBUG) Rlog.d(LOG_TAG,
"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));
mmi.processCode();
// FIXME should this return null or something else?
return null;
}
}
14、GsmCallTracker的dial中,通过mCi对象调用RIL的dial方法
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
synchronized Connection
dial (String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
throws CallStateException {
....
mPendingMO = new GsmConnection(mPhone, checkForTestEmergencyNumber(dialString),
this, mForegroundCall);
mHangupPendingMO = false;
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());
}
....
return mPendingMO;
}
15、最终在RIL.java中通过调用dial下发DIAL命令到qcril,然后qcril通过封装的AT命令下发给modem,这样电话就拨打出去了
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
@Override
public void
dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mParcel.writeString(address);
rr.mParcel.writeInt(clirMode);
if (uusInfo == null) {
rr.mParcel.writeInt(0); // UUS information is absent
} else {
rr.mParcel.writeInt(1); // UUS information is present
rr.mParcel.writeInt(uusInfo.getType());
rr.mParcel.writeInt(uusInfo.getDcs());
rr.mParcel.writeByteArray(uusInfo.getUserData());
}
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
最后画了张图总结下,如果有错误,麻烦大家帮忙纠正,相互学习,由于只能上传图片,如果需要更清楚的图片可以去下面地址下载
http://download.csdn.net/detail/yin1031468524/9720220