一、进入Dialer App
使用adb logcat -vtime -b events命令查看手机的运行日志,最后操作手机,点击dialer进入拨号界面。
05-26 19:20:41.633 I/wm_task_created( 889): [9,-1]
05-26 19:20:41.633 I/wm_stack_created( 889): 9
05-26 19:20:41.641 I/wm_create_task( 889): [0,9]
05-26 19:20:41.641 I/wm_create_activity( 889): [0,155679312,9,com.google.android.dialer/.extensions.GoogleDialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]
05-26 19:20:41.644 I/wm_pause_activity( 889): [0,61395580,com.android.launcher3/.uioverrides.QuickstepLauncher,userLeaving=true]
05-26 19:20:41.686 I/am_uid_running( 889): 10126
05-26 19:20:41.687 I/wm_on_top_resumed_lost_called( 1383): [61395580,com.android.launcher3.uioverrides.QuickstepLauncher,topStateChangedWhenResumed]
05-26 19:20:41.702 I/wm_on_paused_called( 1383): [61395580,com.android.launcher3.uioverrides.QuickstepLauncher,performPause]
05-26 19:20:41.710 I/wm_add_to_stopping( 889): [0,61395580,com.android.launcher3/.uioverrides.QuickstepLauncher,makeInvisible]
05-26 19:20:41.744 I/sysui_multi_action( 889): [757,803,799,window_time_0,802,8]
05-26 19:20:41.778 I/am_proc_start( 889): [0,14079,10126,com.google.android.dialer,pre-top-activity,{com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity}]
05-26 19:20:41.783 I/auditd ( 366): type=1400 audit(0.0:38794): avc: denied { read } for comm="HwBinder:366_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=13719 scontext=u:r:hal_graphics_allocator_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
05-26 19:20:41.861 I/am_proc_bound( 889): [0,14079,com.google.android.dialer]
05-26 19:20:41.867 I/wm_restart_activity( 889): [0,155679312,9,com.google.android.dialer/.extensions.GoogleDialtactsActivity]
05-26 19:20:41.870 I/wm_set_resumed_activity( 889): [0,com.google.android.dialer/.extensions.GoogleDialtactsActivity,minimalResumeActivityLocked]
05-26 19:20:41.906 I/am_uid_active( 889): 10126
05-26 19:20:42.129 I/am_app_pub_providers(14079): 10
05-26 19:20:42.486 I/wm_on_create_called(14079): [155679312,com.google.android.dialer.extensions.GoogleDialtactsActivity,performCreate]
05-26 19:20:42.732 I/wm_on_start_called(14079): [155679312,com.google.android.dialer.extensions.GoogleDialtactsActivity,handleStartActivity]
05-26 19:20:42.738 I/am_create_service( 889): [0,172167006,.ClientApiService,10149,11000]
05-26 19:20:42.779 I/wm_on_resume_called(14079): [155679312,com.google.android.dialer.extensions.GoogleDialtactsActivity,RESUME_ACTIVITY]
05-26 19:20:42.862 I/wm_on_top_resumed_gained_called(14079): [155679312,com.google.android.dialer.extensions.GoogleDialtactsActivity,topStateChangedWhenResumed]
05-26 19:20:42.933 I/auditd ( 366): type=1400 audit(0.0:38795): avc: denied { read } for comm="HwBinder:366_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=13719 scontext=u:r:hal_graphics_allocator_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
05-26 19:20:43.073 I/chatty ( 366): uid=1000(system) allocator@2.0-s identical 1 line
05-26 19:20:43.093 I/auditd ( 366): type=1400 audit(0.0:38797): avc: denied { read } for comm="HwBinder:366_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=13719 scontext=u:r:hal_graphics_allocator_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
05-26 19:20:43.150 I/wm_stop_activity( 889): [0,61395580,com.android.launcher3/.uioverrides.QuickstepLauncher]
05-26 19:20:43.158 I/wm_on_stop_called( 1383): [61395580,com.android.launcher3.uioverrides.QuickstepLauncher,STOP_ACTIVITY_ITEM]
05-26 19:20:43.198 I/sysui_multi_action( 889): [319,252,321,191,322,1473,325,176600,757,761,758,7,759,1,806,com.google.android.dialer,871,com.google.android.dialer.extensions.GoogleDialtactsActivity,904,com.android.launcher3,905,0,945,233,1320,8,1321,5]
05-26 19:20:43.236 I/wm_activity_launch_time( 889): [0,155679312,com.google.android.dialer/.extensions.GoogleDialtactsActivity,1473]
05-26 19:20:43.499 I/sysui_multi_action( 889): [324,0,757,1090,758,12,806,com.google.android.dialer,871,com.google.android.dialer.extensions.GoogleDialtactsActivity,1091,1869]
05-26 19:20:50.445 I/service_manager_stats( 1146): [200,1113,176608824]
05-26 19:20:50.448 I/sysui_multi_action( 1146): [757,931,758,4,759,3,932,0,933,0]
上面是Android系统的events日志。点击电话按钮进入拨号界面,ActivityManagerService将启动com.android.dialer包下的DialtactsActivity。DialtactsActivity运行的进程编号是14079,通过adb shell ps -ef命令查看进程信息命令可以确认相关的进程信息,详情如下:
u0_a126 14079 354 5 19:20:40 ? 00:00:08 com.google.android.dialer
1.1 进入拨号界面DialtactsActivity
详细路径:packages/apps/Dialer/java/com/android/dialer/app/DialtactsActivity.java
其运行的进程名是com.android.dialer。
查看DialtactsActivity类的onCreate方法,找到拨号界面对应的layout界面布局文件dialtacts_activity.xml。
@Override
protected void onCreate(Bundle savedInstanceState) {
Trace.beginSection(TAG + " onCreate");
LogUtil.enterBlock("DialtactsActivity.onCreate");
super.onCreate(savedInstanceState);
firstLaunch = true;
isLastTabEnabled =
ConfigProviderComponent.get(this).getConfigProvider().getBoolean("last_tab_enabled", false);
final Resources resources = getResources();
actionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
Trace.beginSection(TAG + " setContentView");
setContentView(R.layout.dialtacts_activity);
...
dialtacts_activity中的floating_action_button就是打开拨号盘的浮动按钮
...
<android.support.design.widget.FloatingActionButton
android:id="@+id/floating_action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
android:contentDescription="@string/action_menu_dialpad_button"
android:src="@drawable/quantum_ic_dialpad_white_24"
app:elevation="@dimen/floating_action_button_translation_z"
app:backgroundTint="?android:attr/colorAccent"/>
...
继续查看DialtactsActivity类的onClick方法,发现拨号盘浮动按钮的响应事件为调用当前类的showDialpadFragment方法。
@Override
public void onClick(View view) {
int resId = view.getId();
if (resId == R.id.floating_action_button) {
if (!isDialpadShown) {
LogUtil.i(
"DialtactsActivity.onClick", "floating action button clicked, going to show dialpad");
PerformanceReport.recordClick(UiAction.Type.OPEN_DIALPAD);
inCallDialpadUp = false;
showDialpadFragment(true);
PostCall.closePrompt();
} else {
LogUtil.i(
"DialtactsActivity.onClick",
"floating action button clicked, but dialpad is already showing");
}
...
private void showDialpadFragment(boolean animate) {
LogUtil.i("DialtactActivity.showDialpadFragment", "animate: %b", animate);
if (isDialpadShown) {
LogUtil.i("DialtactsActivity.showDialpadFragment", "dialpad already shown");
return;
}
if (stateSaved) {
LogUtil.i("DialtactsActivity.showDialpadFragment", "state already saved");
return;
}
isDialpadShown = true;
listsFragment.setUserVisibleHint(false);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
if (dialpadFragment == null) {
dialpadFragment = new DialpadFragment();
ft.add(R.id.dialtacts_container, dialpadFragment, TAG_DIALPAD_FRAGMENT);
} else {
ft.show(dialpadFragment);
}
dialpadFragment.setAnimate(animate);
Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
ft.commit();
if (animate) {
floatingActionButtonController.scaleOut();
maybeEnterSearchUi();
} else {
floatingActionButtonController.scaleOut();
maybeEnterSearchUi();
}
actionBarController.onDialpadUp();
Assert.isNotNull(listsFragment.getView()).animate().alpha(0).withLayer();
// adjust the title, so the user will know where we're at when the activity start/resumes.
setTitle(R.string.launcherDialpadActivityLabel);
}
当点击拨号盘浮动按钮,弹出拨号盘对应的代码是DialpadFragment.java。
1.2 DialpadFragment拨号盘
在拨号盘发现界面元素的一些规律,可以将其分为三个区域。
• 已输入电话号码显示和控制区域包括拨号控制菜单、已输入电话号码输入框和删除按钮。
• 12个键盘区域包括0~9共10个数字按键、*按键和#按键。
• 拨号按钮
打电话的过程就是在此界面输入电话号码后,单击拨号按键,因此,需要找到拨号按键的onClick事件中的响应逻辑。查找DialpadFragment.java源文件的onClick方法,发现响应拨号按钮点击事件时将调用当前类的handleDialButtonPressed方法,此方法非常简单,用来判断输入的呼叫号码是否合法,非法将弹出异常信息提示用户,合法则执行正常的拨号流程。
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);
//号码空白自动补全
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 {
//拨打进入拨打电话位置。核心,主流程
PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD));
hideAndClearDialpad();
}
}
}
1.3 点击拨号后的流程
android/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/precall/PreCall.java
static void start(Context context, CallIntentBuilder builder) {
DialerUtils.startActivityWithErrorToast(context, getIntent(context, builder));
}
}
DialerUtils.java 主要调用 placeCallOrMakeToast 进入TelecomUtil.placeCall()
android/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/util/DialerUtils.java
public static void startActivityWithErrorToast(
final Context context, final Intent intent, int msgId) {
try {
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);
}
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);//弹出对话框 拨打 WPS 电话会中断现有通话。
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) {
///M: Add for enrich call for OP08.
///It need to set some informations before making an enrich call. @{
ExtensionManager.getDialerOthersExtension().setPrecallInfo(context, 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();
}
}
android/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/telecom/TelecomUtil.java
public static boolean placeCall(Context context, Intent intent) {
if (hasCallPhonePermission(context)) {
getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
return true;
}
return false;
}
根据getTelecomManager()方法,发现是走的是TelecomManager的placeCall()
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
二.Phone进程 接口framework/base/telecom
android/frameworks/base/telecomm/java/android/telecom/TelecomManager.java
@RequiresPermission(anyOf = {android.Manifest.permission.CALL_PHONE,
android.Manifest.permission.MANAGE_OWN_CALLS})
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(), mContext.getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
三.进入System进程 packages/services/Telecomm
进入System进程TelecomService是属于系统框架向外提供的服务,随着系统启动而启动,并由TelecomManager向外提供访问接。在TelecomSystem第一次绑定时(系统初始化的时候会进行绑定,并不是在打电话的时候才绑定),TelecomSystem会被初始化期间构建一些列必须资源,包括Phone和CI(和底层沟通的对象)的构建。
android/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/components/TelecomService.java
@Override
public IBinder onBind(Intent intent) {
Log.d(this, "onBind");
initializeTelecomSystem(this);//系统初始化的时候就会进行绑定,并不是在打电话的时候才绑定),TelecomSystem 会被初始化,期间构建一些列必须资源,包括Phone和CI(和底层沟通的对象)的构建
synchronized (getTelecomSystem().getLock()) {
return getTelecomSystem().getTelecomServiceImpl().getBinder();
}
}
static void initializeTelecomSystem(Context context) {
if (TelecomSystem.getInstance() == null) {
NotificationChannelManager notificationChannelManager =
new NotificationChannelManager();
notificationChannelManager.createChannels(context);
boolean shouldPauseBetweenRingtoneRepeat = context.getResources().getBoolean(
R.bool.should_pause_between_ringtone_repeats);
// TelecomSystem被初始化 构造都是在打电话前执行的
TelecomSystem.setInstance(
new TelecomSystem(
context,
new MissedCallNotifierImpl.MissedCallNotifierImplFactory() {
@Override
public MissedCallNotifierImpl makeMissedCallNotifierImpl(
Context context,
PhoneAccountRegistrar phoneAccountRegistrar,
DefaultDialerCache defaultDialerCache) {
return new MissedCallNotifierImpl(context,
phoneAccountRegistrar, defaultDialerCache);
}
},
.....
.......
........
}
if (BluetoothAdapter.getDefaultAdapter() != null) {
context.startService(new Intent(context, BluetoothPhoneService.class));
}
}
TelecomSystem 系统进程中,通话模块的核心
android/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/TelecomSystem.java
TelecomSystem 构造函数
public TelecomSystem(
Context context,
....
.....
......) {
mContext = context.getApplicationContext();
......
.....
// 构建CallsManager
mCallsManager = new CallsManager(
mContext,
mLock,
mContactsAsyncHelper,
......
........
..........);
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(......)
mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
//注册广播
mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
mContext.registerReceiver(mBootCompletedReceiver, BOOT_COMPLETE_FILTER);
mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
// 构建CallIntentProcessor, 执行Intent的请求
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
mContext, mCallsManager);
// Register the receiver for the dialer secret codes, used to enable extended logging.
mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
// 构建TelecomServiceImpl,这个是TelecomSystem的真正实现,返回的binder由这里提供,
// 外部对TelecomService的操作实际上都是在TelecomServiceImpl里面进行的。
mTelecomServiceImpl = new TelecomServiceImpl(
mContext, mCallsManager, mPhoneAccountRegistrar,
new CallIntentProcessor.AdapterImpl(),
new UserCallIntentProcessorFactory() {
@Override
public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
return new UserCallIntentProcessor(context, userHandle);
}
},
defaultDialerCache,
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
mLock);
Log.endSession();
}
上面的构造都是在打电话之前执行的,下面继续回到调起InCallUi流程
上面placeCall() 方法会进入到 TelecomServiceImpl.java中ITelecomService.Stub中的placeCall()方法,ITelecomService.Stub是一提供一个拨号入口。
android/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
public void placeCall(Uri handle, Bundle extras, String callingPackage,
String callingFeatureId) {
try {
Log.startSession("TSI.pC");
enforceCallingPackage(callingPackage);//权限检查,传入对应的调用位置(调用位置org.codeaurora.dialer)
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, callingFeatureId,
"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, callingFeatureId, "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, callingFeatureId, null)
== 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;
/// M: CTA test, check call forward permission. @{
if (!MtkUtil.checkCallForwardForBinder(mContext,
phoneAccountHandle,
handle.getSchemeSpecificPart(),
callingPackage)) {
Log.w(this, "No call forward permission!");
long token = Binder.clearCallingIdentity();
try {
MtkUtil.showCallForwardPermissionDialog(mContext,
Binder.getCallingUserHandle());
} finally {
Binder.restoreCallingIdentity(token);
}
return;
}
/// M: @}
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();
}
}
接下来UserCallIntentProcessor.java主要处理一些 内部校验一些拨号的权限,以及其它操作权限看看是不是需要弹框拒绝。比如 《只允许打紧急电话,没有电话许可,此应用程序不能发出呼叫》
android/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
// 确保无法调用的设备上没有处理调用意图。
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); //调用processOutgoingCallIntent()方法紧接着执行
}
}
// 核心,主过程 内部校验一些拨号的权限,以及其它操作权限看看是不是需要弹框拒绝。比如 《只允许打紧急电话,没有电话许可,此应用程序不能发出呼叫》
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
// 确保使用TEL方案拨打的sip uri转换为sip方案。
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
// number into the personal dialer.检查DISALLOW_OUTGOING_CALLS限制。注意:我们在托管配置文件用户中跳过此检查,因为总是可以通过复制和粘贴电话号码到个人拨号器来绕过此检查。
if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
// Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
// restriction. 只有使用DISALLOW_OUTGOING_CALLS限制的用户才允许使用紧急呼叫。
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);
intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
// Save the user handle of current user before forwarding the intent to primary user.在将意图转发给主用户之前,保存当前用户的用户handle。
intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
// 核心,主过程 //检查一些权限后,执行那个下面代码发送广播
sendIntentToDestination(intent, isLocalInvocation);
}
/**
* rebroadcasting it. 潜在地将意图传递给仅作为主要用户运行的广播接收器。如果调用方是电信服务的本地调用方,我们将发送意图给电信,而不进行重播。
*/
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(mContext, PrimaryCallReceiver.class); // //开启广播 最后进入到 PrimaryCallReceiver
if (isLocalInvocation) {
Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
synchronized (TelecomSystem.getInstance().getLock()) {
TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
}
} else {
Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); //发送广播
}
return true;
}