(O)Telephony分析之通话流程分析(二)拨打电话流程分析(上)
拨打电话始于通话拨号盘的过程,在DialpadFragment组件中展示该界面。因此需要深入探讨这个地方
一.拨号盘界面拨号流程
public void onClick(View view) {
......
if (resId == R.id.dialpad_floating_action_button) {
view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
// 处理Dial Button点击事件
handleDialButtonPressed();
}
......
}
通过调用handleDialButtonPressed方法,处理点击拨号按钮后的流程
private void handleDialButtonPressed() {
// 没有输入号码的情况下,暂不讨论
if (isDigitsEmpty()) { // No number entered.
handleDialButtonClickWithEmptyDigits();
} else {
......
// uri加上tel前缀,并且type设置为INITIATION_DIALPAD,生成URI
final Intent intent = new CallIntentBuilder(number).setCallInitiationType(LogState.INITIATION_DIALPAD).build();
// Leo, 尝试去启动一个Activity,如果启动不了,会弹出一个 "Activity is not found" 的Toast框
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
// Leo,隐藏拨号盘,并且清除号码盘上的号码
hideAndClearDialpad(false);
}
}
可以看到,在此处初始化CallIntentBuilder后调用其build方法以生成一个Intent对象,并将其作为参数传递给DialerUtils的startActivityewithToast方法。
public static class CallIntentBuilder {
......
public CallIntentBuilder(Uri uri) {
mUri = uri;
}
public CallIntentBuilder(String number) {
this(CallUtil.getCallUri(number));
}
public CallIntentBuilder setCallInitiationType(int initiationType) {
mCallInitiationType = initiationType;
return this;
}
public Intent build() {
// Leo, 初始化一个Intent
return getCallIntent(
mUri,
mPhoneAccountHandle,
mIsVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY,
mCallInitiationType);
}
}
从这边观察到,在这边可以看到首先通过构造方法将number转换成Uri对象。接着使用build方法调用getCallIntent函数以获取intent。根据上述信息可知:知道getCallIntent函数接收以下三个关键参数:第一部分是一个刚生成的Uri对象,并且该对象以tel:格式开头;第二部分因未设置该字段而导致为空;第三部分因未配置视频通话功能而设为空,默认采用AudioOnly状态;第四部分则来自用户传入的信息LogState.INITIATION_DIALPAD。
public static Intent getCallIntent(
Uri uri, PhoneAccountHandle accountHandle, int videoState, int callIntiationType) {
// 初始化一个Intent,action为CALL_ACTION,Data为uri
final Intent intent = new Intent(CALL_ACTION, uri);
// 目前应该是没有videoState的,即为VideoProfile.STATE_AUDIO_ONLY
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
final Bundle b = new Bundle();
// 设置其INITIATION_TYPE,此处第一次为LogState.INITIATION_DIALPAD
b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
// 设置其Extra
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);
// 此前没有设置,应该为null
if (accountHandle != null) {
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
}
return intent;
}
核查Intent中所包含的信息
将会执行DialerUtils的startActivityWithErrorToast方法
public static void startActivityWithErrorToast(Context context, Intent intent) {
startActivityWithErrorToast(context, intent, R.string.activity_not_available);
}
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
try {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {
......
// 调用TelecomUtil的placeCall函数,传入的参数为intent
final boolean hasCallPermission = TelecomUtil.placeCall((Activity) 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);
}
}
......
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
调用TelecomUtil的placeCall方法
二.Telecom阶段
public static boolean placeCall(Activity activity, Intent intent) {
if (hasCallPhonePermission(activity)) {
// 调用TelecomManagerCompat的placeCall函数,注意其传入的参数
// 第一个参数为DialpadFragment中的getActivity
// 第二个参数为TelecomManager对象
// 第三个参数中包含了拨号的所有参数
TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
return true;
}
return false;
}
(一)getTelecomManager是什么
看下其代码
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
此前, 我们曾对get ${appname}进行了研究, 是在 ${appnameRegistry}中进行了注册, 如下:
registerService(Context.TELECOM_SERVICE, TelecomManager.class,
new CachedServiceFetcher<TelecomManager>() {
@Override
public TelecomManager createService(ContextImpl ctx) {
return new TelecomManager(ctx.getOuterContext());
}});
其返回的是新初始化的一个TelecomManager对象,请详细查看该对象的具体构造方法
public TelecomManager(Context context) {
this(context, null);
}
public TelecomManager(Context context, ITelecomService telecomServiceImpl) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
mTelecomServiceOverride = telecomServiceImpl;
android.telecom.Log.initMd5Sum();
}
该处TelecomManager对象中的telecomServiceImpl字段值等于null。(二) TelecomManagerCompat中placeCall方法的详细解析
public static void placeCall(@Nullable Activity activity,
@Nullable TelecomManager telecomManager, @Nullable Intent intent) {
......
// Android M以上的版本
if (CompatUtils.isMarshmallowCompatible()) {
// 调用了TelecomManager对象的placeCall函数
// 第一个参数为包含号码的URI
telecomManager.placeCall(intent.getData(), intent.getExtras());
return;
}
activity.startActivityForResult(intent, 0);
}
属于O类项目的任务,在优先级上比M类项目的任务更高时,则会调用telecomManager中的place call方法;而telecomManager是一个刚刚被获取到的TelecomManager实例。
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
......
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
在之前的分析中, TelecomManager 的 getTelecomService 方法在 telecomServiceImpl 为 null 时返回 TelecomManagerImpl 的 mBinderImpl, 并从而实现了 mBinderImpl 的 placeCall 功能.
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
try {
......
// 判断是否拥有拨打电话的权限
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);
if (extras != null) {
extras.setDefusable(true);
intent.putExtras(extras);
}
// mUserCallIntentProcessorFactory的create方法是在TelecomSystem的构造方法中重写的
// 返回的是UserCallIntentProcessor对象
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, isSelfManaged ||
(hasCallAppOp && hasCallPermission));
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
......
}
通过mUserCallIntentProcessorFactory工厂接口实现意图处理功能,并将返回的对象进行intent处理操作,请问该类的具体实现位置在哪里?
public TelecomServiceImpl(
Context context,
CallsManager callsManager,
PhoneAccountRegistrar phoneAccountRegistrar,
CallIntentProcessor.Adapter callIntentProcessorAdapter,
UserCallIntentProcessorFactory userCallIntentProcessorFactory,
DefaultDialerCache defaultDialerCache,
SubscriptionManagerAdapter subscriptionManagerAdapter,
TelecomSystem.SyncRoot lock) {
......
mUserCallIntentProcessorFactory = userCallIntentProcessorFactory;
......
}
这表明该对象在初始化过程中接收了必要的参数。
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);
因此,在UserCallIntentProcessorFactory的create方法中,一个UserCallIntentProcessor实例被成功生成。由此导致的结果是TelecomManager的placeCall方法最终触发了该实例的intent processing method
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
......
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);
}
}
鉴于之前传递给 Intent 的对象被指定为 Intent.ACTION_CALL 的情况下,则会导致调用 processOutgoingCallIntent 这一行为发生。在此处值得注意的是:其传递给该方法的具体参数是什么?具体来说:第一个参数是一个包含数据的对象;第二个参数是 callingPackageName,在 TelecomManager 中通过 mContext.getOpPackageName() 获取;其中 mContext 是通过 DialpadFragment 中 getActivity 方法获得;因此它的值非常明确;第三个参数是 canCallNonEmergency,在 TelecomServiceImpl 类中进行定义;这一属性无需多言;接下来将深入分析 processOutgoingCallIntent 方法的行为机制
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
Uri handle = intent.getData(); // tel:号码
String scheme = handle.getScheme(); // tel
String uriString = handle.getSchemeSpecificPart();
......
// 通话状态,默认是语音通话
int videoState = intent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
// Leo, 传入的package是否为系统默认的Dialer应用
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);
}
该mUserHandle暂不对这部分代码进行讨论。
private boolean sendBroadcastToReceiver(Intent intent) {
// 非incoming call
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;
}
实际上,并非只是传递通话的基本信息到PrimaryCallReceiver进行操作
public void onReceive(Context context, Intent intent) {
......
synchronized (getTelecomSystem().getLock()) {
// 接收到消息后处理,调用TelecomSystem的getCallIntentPRocessor方法,获取CallIntentProcessor对象
// 并且调用起的processIntent方法
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
......
}
public TelecomSystem getTelecomSystem() {
return TelecomSystem.getInstance();
}
这个TelecomSystem类中的getInstantiationMethod方法调用后会生成一个新的TelecomSystem对象,并即是我们之前所了解过的这种对象类型
public CallIntentProcessor getCallIntentProcessor() {
return mCallIntentProcessor;
}
TelecomSystem的构造函数中
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
初始化CallIntentProcessor对象,并调用其processIntent方法
public void processIntent(Intent intent) {
// 默认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);
}
Trace.endSection();
}
请调用该processOutgoingCallIntent方法,并特别注意其中的参数mCallsManager是在TelecomSystem初始化CallIntentProcessor时作为参数传递给该方法的
