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 {
startCall(number, OriginCodes.DIALPAD_DIRECT_DIAL);//这里是新增内容
}
}
}
private void startCall(String number, String origin) {
if (mCurrentCallMethodInfo != null && mCurrentCallMethodInfo.mIsInCallProvider &&
!PhoneNumberUtils.isEmergencyNumber(number)) {
mCurrentCallMethodInfo.placeCall(origin, number, getActivity(), false, true,
new StartInCallCallReceiver.InCallCallListener() {
@Override
public void onResult(int resultCode) {
if (resultCode == StatusCodes.StartCall.CALL_CONNECTED) {
hideAndClearDialpad(false);
}
}
});
} else {
// If no sim is selected, or emergency callmethod selected, or number is
// an emergency number, phone account handle should be null, and will use the
// default account.
// Else, create PhoneAccountHandle from selected callmethod components and
// initial call using that account.
PhoneAccountHandle handle = CallMethodInfo.getPhoneAccountHandleFromCallMethodInfo(
getActivity(), mCurrentCallMethodInfo, number);
final Intent intent = IntentUtil.getCallIntent(number,//返回的action:android.intent.action.CALL而在5.1上是android.intent.action.CALL_PRIVILEGED
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity())
.getCallOrigin() : null),
handle);
DialerUtils.startActivityWithErrorToast(getActivity(), intent, origin);//虽然增加了一步startCall,但这里还是调用了方法
hideAndClearDialpad(false);
}
}
public static void startActivityWithErrorToast ( Context context , Intent intent , int msgId ,
String origin) {
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();//geiPoint(),对应的setPoint()方法,在点击的时候调用
if (touchPoint.x != 0 || touchPoint.y != 0) {
Bundle extras = new Bundle();
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);//是与动画有关咯,将传至InCallUI
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
if (origin != null) {
intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, origin);
}
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());//直接到TelecomManager里去了。在L 5.1上是调用startActivityForResult(),之后再启动CallActivity
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
/**
* Places a new outgoing call to the provided address using the system telecom service with
* the specified extras.
*
* This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
* except that the outgoing call will always be sent via the system telecom service. If
* method-caller is either the user selected default dialer app or preloaded system dialer
* app, then emergency calls will also be allowed.
*
* Requires permission: {@link android.Manifest.permission#CALL_PHONE}
*
* Usage example://举例
* <pre>
* Uri uri = Uri.fromParts("tel", "12345", null);//Uri 主要是号码
* Bundle extras = new Bundle();
* extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);//默认开扬声器
* telecomManager.placeCall(uri, extras);
* </pre>
*
* The following keys are supported in the supplied extras.
* <ul>
* <li>{@link #EXTRA_OUTGOING_CALL_EXTRAS}</li>
* <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
* </ul>
*
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
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);
}
}
}
/**
* @see android.telecom.TelecomManager#placeCall
*/
@Override
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);
}
}
}
/**
* Processes intents sent to the activity.
*
* @param intent The intent.
*/
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
//略过
String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) || //满足三种CALL_ACTION的任何一种都往外拨打
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
}
}
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);
//
videoState = 0 意为AUDIO_ONLY 1和2为单向视频,3为双向视频
if (!isEmergencyVideoCallingSupported() && VideoProfile.isVideo(videoState)
&& TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
Log.d(this, "Emergency call...Converting video call to voice...");
videoState = VideoProfile.STATE_AUDIO_ONLY;
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
videoState);
}
if (VideoProfile.isVideo(videoState) && isTtyModeEnabled() &&
!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.video_call_not_allowed_if_tty_enabled);
Log.d(this, "Rejecting video calls as tty is enabled");
return;
}
intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
sendBroadcastToReceiver(intent);
}
/**
* Trampolines the intent to the broadcast receiver that runs only as the primary user.
*/
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;
}
@Override
public void onReceive(Context context, Intent intent) {
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
}
D/Telecom (11638): UserCallIntentProcessor: processOutgoingCallIntent videoState = 0 //
videoState = 0 意为AUDIO_ONLY 1和2为单向视频,3为双向视频D/Telecom (11638): UserCallIntentProcessor: Sending broadcast as user to CallReceiver
I/Telecom (11638): CallIntentProcessor: onReceive - isUnknownCall: false
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();
}
/**
* Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
*
* @param intent Call intent containing data about the handle to call.
*/
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
Bundle clientExtras = null;
String origin = null;
//掠过一些本次不关系的
Log.i(CallIntentProcessor.class, " processOutgoingCallIntent handle = " + handle
+ ",scheme = " + scheme + ", uriString = " + uriString
+ ", isSkipSchemaParsing = " + isSkipSchemaParsing
+ ", isAddParticipant = " + isAddParticipant
+ ", isCallPull = " + isCallPull);
// 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 boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns //先把InCallUI界面起来
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras,
origin);
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);
}
}
}
/**
* Processes the supplied intent and starts the outgoing call broadcast process relevant to the
* intent.
*
* This method will handle three kinds of actions:
*
* - CALL (intent launched by all third party dialers)//第三方的app拨号应用
* - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer)//系统拨号应用,语音拨号应用
* - CALL_EMERGENCY (intent launched by lock screen emergency dialer)//锁屏界面的紧急拨号器拨打的
*
* @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate
* {@link DisconnectCause} if the call did not, describing why it failed.
*/
int processIntent() {
Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");
Intent intent = mIntent;
String action = intent.getAction();
final Uri handle = intent.getData();
//略
final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);
rewriteCallIntentAction(intent, isPotentialEmergencyNumber);//这里是重写CALL_ACTION的方法,具体的见下面的插播
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;//一个较为关键的值,赋值受CALL_ACTION影响
if (Intent.ACTION_CALL.equals(action)) {//在目前的版本上默认是这个没啥说的
if (isPotentialEmergencyNumber) {//是紧急号码的话继续,不是紧急号码的话,那进来就什么都没做。至于紧急号码的判断,在流程中不同的地方可能不一样,请自行了解
if (!mIsDefaultOrSystemPhoneApp) {
Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
+ "unless caller is system or default dialer.", number, intent);
launchSystemDialer(intent.getData());//第三方输入紧急号码后点击拨打会调到系统拨号程序(如果不是紧急号码的话会直接跳到系统拨号界面,也就是InCallUI)
return DisconnectCause.OUTGOING_CANCELED;
} else {
callImmediately = true;//走到这里的条件是:1.重写后的ACTION是CALL 2.是Potential紧急号码 3.由系统拨号应用发起的拨号
}
}
} else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {//紧急号码的CALL_ACTION
if (!isPotentialEmergencyNumber) {
Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
+ "Intent %s.", number, intent);
return DisconnectCause.OUTGOING_CANCELED;//用紧急号码的action拨打非紧急号码,是不允许的,返回。
}
callImmediately = true;//到这里的条件是:就是action是
android.intent.action.CALL_EMERGENCY} else {
Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
return DisconnectCause.INVALID_NUMBER;
}
if (callImmediately) {//只有在紧急号码的情况下进入
Log.i(this, "Placing call immediately instead of waiting for "
+ " OutgoingCallBroadcastReceiver: %s", intent);
String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
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);
mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,//直接开始拨打了,可以说这段代码在一定程度上会加快拨打紧急号码的速度
speakerphoneOn, videoState);
// Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
// so that third parties can still inspect (but not intercept) the outgoing call. When
// the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
// initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
}
Log.i(this, "Sending NewOutgoingCallBroadcast for %s", mCall);
if (isSkipSchemaParsing) {
broadcastIntent(intent, handle.toString(), !callImmediately);
} else {
broadcastIntent(intent, number, !callImmediately);//又来广播
}
return DisconnectCause.NOT_DISCONNECTED;
}
V/Telecom (11638): NewOutgoingCallIntentBroadcaster: processIntent isConferenceUri: false isSkipSchemaParsing = false
/**
* Given a call intent and whether or not the number to dial is an emergency number, rewrite
* the call intent action to an appropriate one.
*
* @param intent Intent to rewrite the action for
* @param isPotentialEmergencyNumber Whether or not the number is potentially an emergency
* number.
*/
private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) {
String action = intent.getAction();
/* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */注释已经很明确了
if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {//判断条件是 ACTION_CALL_PRIVILEGED,当前M默认为CALL,所以这个方法基本上也是废的。
if (isPotentialEmergencyNumber) {//紧急号码的话重写成android.intent.action.CALL_EMERGENCY
Log.i(this, "ACTION_CALL_PRIVILEGED is used while the number is a potential"
+ " emergency number. Using ACTION_CALL_EMERGENCY as an action instead.");
action = Intent.ACTION_CALL_EMERGENCY;
} else {
action = Intent.ACTION_CALL;//否则重写成
android.intent.action.CALL}
Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action);
intent.setAction(action);
}
}
/**
* Sends a new outgoing call ordered broadcast so that third party apps can cancel the
* placement of the call or redirect it to a different number.
*
* @param originalCallIntent The original call intent.
* @param number Call number that was stored in the original call intent.
* @param receiverRequired Whether or not the result from the ordered broadcast should be
* processed using a {@link NewOutgoingCallIntentBroadcaster}.
*/
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
}
/**
* Processes the result of the outgoing call broadcast intent, and performs callbacks to
* the OutgoingCallIntentBroadcasterListener as necessary.
*/
private class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
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));
//略
GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,??
EXTRA_START_CALL_WITH_SPEAKERPHONEfalse),
mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY));
Trace.endSection();
}
}
I/Telecom (11638): Event: Call 2: CREATED, null
I/Telecom (11638): Event: Call 2: SET_CONNECTING, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [356a192b7913b04c54574d18c28d46e6395428ab], UserHandle{0}
I/Telecom (11638): Event: Call 2: AUDIO_ROUTE, EARPIECE//audio变化
I/Telecom (11638): Event: Call 2: BIND_CS, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}//CS call bind
I/Telecom (11638): Event: Call 2: CS_BOUND, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}
I/Telecom (11638): Event: Call 2: START_CONNECTION, tel:10010
I/Telecom (11638): Event: Call 2: SET_DIALING, successful outgoing call//拨号
I/Telecom (11638): Event: Call 2: SET_ACTIVE, active set explicitly//接通
I/Telecom (11638): Event: Call 2: REQUEST_DISCONNECT, null//主动断开
I/Telecom (11638): Event: Call 2: SET_DISCONNECTED, disconnected set explicitly> DisconnectCause [ Code: (LOCAL) Label: () Description: () Reason: (LOCAL) Tone: (27) ]//断开原因
I/Telecom (11638): Event: Call 2: DESTROYED, null