在拨号盘Dialer中点击拨号按钮
packages\apps\Dialer\java\com\android\dialer\dialpadview\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()) {
digits.setCursorVisible(true);
}
} else if (resId == R.id.dialpad_overflow) {
//显示dialPad的悬浮按钮
overflowPopupMenu.show();
} else {
LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);
}
}
handleDialButtonPressed()方法中,对获取到的字符串进行校验,并创建intent。最终调用PreCall的start();—区别P版本上直接调用DialUntils
private void handleDialButtonPressed() {
//对号码输入框获取到的字符串长度判断
if (isDigitsEmpty()) {
// No number entered.
// No real call made, so treat it as a click
PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
//这个地方主要是区别CDMA做了些操作,非CDMA的phone,这个地方会填充上次的拨号号码,用户需要再次手动触发才能进行拨号
handleDialButtonClickWithEmptyDigits();
} else {
final String number = digits.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(prohibitedPhoneNumberRegexp)
&& number.matches(prohibitedPhoneNumberRegexp)) {
PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
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 {
//这里会将intent传递给PreCall,由PreCall调用DialerUtils的start区别于P上直接调用
PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD));
hideAndClearDialpad();//隐藏拨号盘,并清理号码输入框的内容
}
}
}
packages\apps\Dialer\java\com\android\dialer\precall\PreCall.java
Precall中start()只是调用到DialerUtils中startActivityWithErrorToast();没有实质性的处理逻辑
static void start(Context context, CallIntentBuilder builder) {
DialerUtils.startActivityWithErrorToast(context, getIntent(context, builder));
}
packages\apps\Dialer\java\com\android\dialer\util\DialerUtils.java
startActivityWithErrorToast()做了一系列的非法判断,最终将intent发出去到TeleCommon
public static void startActivityWithErrorToast(
final Context context, final Intent intent, int msgId) {
try {
//对intent中的action进行判断,如果是call则进一步处理,不是的话直接startActivity()
if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
// 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;
// Make sure to not accidentally clobber any existing extras
if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
} else {
extras = new Bundle();
}
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
//注意这个extra,这个在后续创建incoming还是outgoing的连接使
}
//这个地方对当前的网络模式和通话状态进行判断,是否需要弹框提醒用户。
if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
LogUtil.i(
"DialUtils.startActivityWithErrorToast",
"showing outgoing WPS dialog before placing call");
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.outgoing_wps_warning);
//确定按钮点击事件,操作和正常逻辑一致
builder.setPositiveButton(
R.string.dialog_continue,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
placeCallOrMakeToast(context, intent);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().show();
} else {
//正常逻辑
placeCallOrMakeToast(context, intent);
}
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
private static void placeCallOrMakeToast(Context context, Intent intent) {
//通过调用placeCall的返回结果,来处理是否需要弹框提示没有权限
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();
}
}
DialerUtils调用TelecomUtils的placeCall()
public static boolean placeCall(Context context, Intent intent) {
if (hasCallPhonePermission(context)) {
getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
return true;
}
return false;
}
通过serviceManager获取TelecomManager过程
private static TelecomManager getTelecomManager(Context context) {
//ServiceManager在service注册的时候调用addService()将service维护在列表中
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
二、拨号流程到达TeleCom
通过binder获取到ITelecomService的具体实现,并继续向下调用placeCall()
public void placeCall(Uri address, Bundle extras) {
//binder间调用,获取回来的是TeleService的proxy,通过proxy调用service
ITelecomService service = getTelecomService();
if (service != null) {
if (address == null) {
Log.w(TAG, "Cannot place call to empty address.");
}
try {
//再调用TeleComServiceImpl的placeCall()
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
获取 ITelecomService.Stub具体的实现类
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
}
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
ITelecomService 的实现类实际上就是TelecomServiceImpl.java,这个时候实际上从FWK已经到了应用层
packages\services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java
@Override
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
try {
Log.startSession("TSI.pC");
enforceCallingPackage(callingPackage);
PhoneAccountHandle phoneAccountHandle = null;
if (extras != null) {
phoneAccountHandle = extras.getParcelable(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
if (extras.containsKey(TelecomManager.EXTRA_IS_HANDOVER)) {
// This extra is for Telecom use only so should never be passed in.
extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
}
}
boolean isSelfManaged = phoneAccountHandle != null &&
isSelfManagedConnectionService(phoneAccountHandle);
if (isSelfManaged) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS,
"Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
if (!callingPackage.equals(
phoneAccountHandle.getComponentName().getPackageName())
&& !canCallPhone(callingPackage,
"CALL_PHONE permission required to place calls.")) {
// The caller is not allowed to place calls, so we want to ensure that it
// can only place calls through itself.
throw new SecurityException("Self-managed ConnectionServices can only "
+ "place calls through their own ConnectionService.");
}
} 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;
// The Emergency Dialer has call privileged permission and uses this to place
// emergency calls. We ensure permission checks in
// NewOutgoingCallIntentBroadcaster#process pass by sending this to
// Telecom as an ACTION_CALL_PRIVILEGED intent (which makes sense since the
// com.android.phone process has that permission).
final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;
synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(hasCallPrivilegedPermission ?
Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
if (extras != null) {
extras.setDefusable(true);
intent.putExtras(extras);
}
//正常的拨号流程
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, isSelfManaged ||
(hasCallAppOp && hasCallPermission),
true /* isLocalInvocation */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}
packages\services\Telecomm\src\com\android\server\telecom\components\UserCallIntentProcessor.java
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
// 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,
isLocalInvocation);
}
}
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
// Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
// Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this check in 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 (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.outgoing_call_not_allowed_user_restriction);
Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
+ "restriction");
return;
} else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
final DevicePolicyManager dpm =
mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
return;
}
final Intent adminSupportIntent = dpm.createAdminSupportIntent(
UserManager.DISALLOW_OUTGOING_CALLS);
if (adminSupportIntent != null) {
mContext.startActivity(adminSupportIntent);
}
return;
}
}
}
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);
// Save the user handle of current user before forwarding the intent to primary user.
intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
}
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,
String callingPackage) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (isLocalInvocation) {
// We are invoking this from TelecomServiceImpl, so TelecomSystem is available. Don't
// bother trampolining the intent, just sent it directly to the call intent processor.
// TODO: We should not be using an intent here; this whole flows needs cleanup.
Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
synchronized (TelecomSystem.getInstance().getLock()) {
TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,
callingPackage);
}
} else {
// We're calling from the UserCallActivity, so the TelecomSystem is not in the same
// process; we need to trampoline to TelecomSystem in the system server process.
Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
tm.handleCallIntent(intent, callingPackage);
}
return true;
}
这里又回到了FWK中
packages\services\Telecomm\src\com\android\server\telecom\CallIntentProcessor.java
public void processIntent(Intent intent, String callingPackage) {
//正常的拨号流程并没有对KEY_IS_UNKNOWN_CALL进行赋值,所以默认是false
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, callingPackage,
mDefaultDialerCache);
}
Trace.endSection();
}
static void processOutgoingCallIntent(
Context context,