Telephony 拨号流程

一、进入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;
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值