Advertisement

(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时作为参数传递给该方法的

全部评论 (0)

还没有任何评论哟~