Phone拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)

 

Phone调用InCallUi流程

流程图

主要的类以及作用


Dialer
Dialer\com\android\dialer\app\dialpad\DialpadFragment.java  拨号键盘
Dialer\java\com\android\dialer\util\DialerUtils.java                    拨号工具
Dialer\java\com\android\dialer\telecom\TelecomUtil.java         通讯工具 
Dialer\java\com\android\incallui\InCallServiceImpl.java             InCallUi开启服务,他继承的是InCallService


framework/base/telecom
framework\base\telecomm\java\android\telecom\TelecomManager.java   向外部提供接口,有很多地方会调用这个接口进行拨号


service/telecomm
services\Telecomm\src\com\android\server\telecom\components\TelecomService.java  系统初始化的时候就会进行绑定,并不                                                                                                                                              是在打电话的时候才绑TelecomSystem                                                                                                                                              会被初始化,期间构建一些列必须源,                                                                                                                                              包括Phone和CI(和底层沟通的对象)                                                                                                                                               的构建
services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java               提供Binder接口
services\Telecomm\src\com\android\server\telecom\components\UserCallIntentProcessor.java  用户拨号处理,处理 内部校验                                                                                                                                                            一些拨号的权限,以及其它作                                                                                                                                                              权限看看是不是需要弹框绝。                                                                                                                                                             比如 《只允许打紧急电话没                                                                                                                                                               有电话许可,此应用程序不能                                                                                                                                                                发 出呼叫》
services\Telecomm\src\com\android\server\telecom\components\PrimaryCallReceiver.java   广播,作为拨号的单一入口, 其到                                                                                                                                                      消息后,就转发给 TelecomSystem                                                                                                                                                     的
services\Telecomm\src\com\android\server\telecom\CallIntentProcessor.java          判断Intent里面的数据,比如  号码是否为空                                                                                                                                       等等....
services\Telecomm\src\com\android\server\telecom\CallsManager.java            官方声明  :CallManager对象为 Phone应用程序                                                                                                                                提供通用 Call API的方法。 SipPhone类实现                                                                                                                                    Phone接口,但大部分由虚拟方法填充,实际现                                                                                                                               使用的是 SipManager,访问 SIP通信框架并控制                                                                                                                              SIP Call
services\Telecomm\src\com\android\server\telecom\InCallController.java             控制电话App的逻辑和UI 

 

流程图:

 

 

接下来开看代码逻辑 一个一个进程看

概述:拨号调用InCallUi模块的过程,如果按照进程来分,可以分成3个模块。

  1. com.android.dialer进程,电话APP模块                路径  : android/vendor/codeaurora/commonsys/packages/apps/Dialer 
  2. com.android.dialer.InCallUi  呼出呼入页面         路径  :要看厂商是否分离了,如果是高通代码就放在Dialer App中
  3. System进程,系统框架层    Service/telecom层  路径  :android/packages/service
  4. com.android.phone进程,    framework/  :       路径  : android/framework

拨号过程由App开始(或第三方调用Telephony),进入接口System进程获取接口,System接口会phone的一个Binder中的一个方法进行处理,进入Phone后做一些判断(比如:号码是否正确), 在启动InCallUil设置状态 ,状态改变 是通过Call对象进行变更

在电话中java层有三个Call对象  所在位置是  

  1. 路径:android/packages/services/Telecomm 
  2. 路径:android/frameworks/base/telecomm
  3. 路径:android/frameworks/opt/telephony

插入一个话题,InCallUi  就来电去电时展示,如果想实现一个InCallUi自己也是可以做到的,比如第三方APP  来电秀,来电视屏等等..都是继承了一个服务(InCallService,给第三方扩展使用),屏蔽调InCallUi所继承的服务

大致流程图:

 

Phone进程是

 

开始代码流程:

 

第一步进入Dialer App

Dialer\java\com\android\dialer\app\dialpad\DialpadFragment.java  属于Dialer拨号盘

 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 = mDigits.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(mProhibitedPhoneNumberRegexp)
          && number.matches(mProhibitedPhoneNumberRegexp)) {
        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 {
        final Intent intent =
            new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
         DialerUtils.startActivityWithErrorToast(getActivity(), intent);  //拨打进入拨打电话位置。核心,主流程
        if(mSubscriptionManager.getActiveSubscriptionInfoCount() != 0) {
          hideAndClearDialpad(false);
        }
// 		hideAndClearDialpad(false);
      }
    }
  }

DialerUtils.java  组要调用  placeCallOrMakeToast 进入TelecomUtil.placeCall()

 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) {
    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();
    }
  }

TelecomUtil.java  主要是调用TelecomManage.placeCall()方法

  public static boolean placeCall(Context context, Intent intent) {
    if (hasCallPhonePermission(context)) {
        // 核心,主过程  这个方法主要是调用TelecomManager.placeCall();//TelecomManager.java属于Framework/Telephony代码
      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
      return true;
    }
    return false;
  }

第二步,Phone进程 接口framework/base/telecom

TelecomManager.java

 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());//调用packager/service/telecom中的Binder
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
            }
        }
    }

第三部 进入System进程 packages/services/Telecomm

进入System进程TelecomService是属于系统框架向外提供的服务,随着系统启动而启动,并由TelecomManager向外提供访问接。在TelecomSystem第一次绑定时(系统初始化的时候会进行绑定,并不是在打电话的时候才绑定),TelecomSystem会被初始化期间构建一些列必须资源,包括Phone和CI(和底层沟通的对象)的构建

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 系统进程中,通话模块的核心

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是一提供一个拨号入口。

TelecomServiceImpl.java

        @Override
        public void placeCall(Uri handle, Bundle extras, String callingPackage) {
            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,
                            "CALL_PHONE permission required to place calls.")) {

                        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");
                }



                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;

                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();
            }
        }

接下来UserCallIntentProcessor.java主要处理一些 内部校验一些拨号的权限,以及其它操作权限看看是不是需要弹框拒绝。比如 《只允许打紧急电话,没有电话许可,此应用程序不能发出呼叫》

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;
    }

接下来进入关播PrimaryCallReceiver中  ,PrimaryCallReceiver作为拨号的单一入口,其收到消息后就转给TelecomSystem中的CallIntentProcessor进行处理

PrimartCallReceiver.java
 

@Override
    public void onReceive(Context context, Intent intent) {//作为拨号的单一入口, 其收到消息后,就转发给 TelecomSystem的
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);//调用所有呼出和呼入电话的单一入口
        }
        Log.endSession();
    }

CallIntentProcessor.java

public void processIntent(Intent intent) {
        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();
    }

    static void processOutgoingCallIntent(
            Context context,
            CallsManager callsManager,
            Intent intent) {

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();
        boolean isSkipSchemaParsing = intent.getBooleanExtra(
                TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false);
        Log.d(CallIntentProcessor.class, "isSkipSchemaParsing = " + isSkipSchemaParsing);


        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString) && !isSkipSchemaParsing) {
            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
        }

        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);

        Bundle clientExtras = null;
        if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
            clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
        }
        if (clientExtras == null) {
            clientExtras = new Bundle();
        }
        if (isSkipSchemaParsing) {
            clientExtras.putBoolean(TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING,
                    isSkipSchemaParsing);
            handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, handle.toString(), null);
        }
        boolean isConferenceUri = intent.getBooleanExtra(
                TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false);
        Log.d(CallIntentProcessor.class, "isConferenceUri = "+isConferenceUri);
        if (isConferenceUri) {
            clientExtras.putBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, isConferenceUri);
        }
        boolean isAddParticipant = intent.getBooleanExtra(
                TelephonyProperties.ADD_PARTICIPANT_KEY, false);
        Log.d(CallIntentProcessor.class, "isAddparticipant = "+isAddParticipant);
        if (isAddParticipant) {
            clientExtras.putBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, isAddParticipant);
        }

        if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
            String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
            clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
        }

        final int callDomain = intent.getIntExtra(
                QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_AUTOMATIC);
        Log.d(CallIntentProcessor.class, "callDomain = " + callDomain);
        clientExtras.putInt(QtiCallConstants.EXTRA_CALL_DOMAIN, callDomain);

        final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);

        if (!callsManager.isSelfManaged(phoneAccountHandle,
                (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
            boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
            // Show the toast to warn user that it is a personal call though initiated in work
            // profile.
            if (fixedInitiatingUser) {
                Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
            }
        } else {
            Log.i(CallIntentProcessor.class,
                    "processOutgoingCallIntent: skip initiating user check");
        }

        Log.d(CallIntentProcessor.class, " processOutgoingCallIntent handle = " + handle
                + ", scheme = " + scheme + ", uriString = " + uriString
                + ", isSkipSchemaParsing = " + isSkipSchemaParsing
                + ", isAddParticipant = " + isAddParticipant);

        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);

        /**
         * description  帐号选择
         */
        /**start*/
        TelecomManager telecomManager =
                (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
        List<PhoneAccountHandle> mSubscriptionAccountHandles = telecomManager.getCallCapablePhoneAccounts();

        boolean isDualCard = (mSubscriptionAccountHandles != null && mSubscriptionAccountHandles.size() > 1);
        if(isDualCard) {

            String phoneNumber = intent.getData().getSchemeSpecificPart();
            phoneNumber = PhoneNumberUtils.stripSeparators(phoneNumber);
            int simGroup = SimSelectDialog.getCardByNumber(context, phoneNumber) - 1;
            if (simGroup >= 0) {

                if (phoneAccountHandle == null) {
                    phoneAccountHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
                }
                if (phoneAccountHandle != null) {
                    if (!phoneAccountHandle.equals(mSubscriptionAccountHandles.get(simGroup))) {

                        ......
        }
        /**end*/
        // 创建一个call.startOutgoingCall 新建一个(或者重用) call, 将CallsManager绑定监听该 call, 然后通知 callsManager 的监听者, 添加了一个 call
        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent);
        //这是一个断点,因为下面将会详细地深入分析 callsManager.startOutgoingCall 的流程,下面分析 startOutgoingCall 完成后, 再从 NewOutgoingCallIntentBroadcaster.processIntent()往下分析
        if (call != null) {
            sendNewOutgoingCallIntent(context, call, callsManager, intent);//拿到上面创建的call, 进行下一步
        }
    }

这里重点看callsManager.startOutgoingCall(......);这里就是调起InCallUi的入口,startOutgoingCall新建一个(或者重用)call,将CallManager绑定监听该call,然后通知callsManager的监听,添加了一个call

CallManager.java
 

    public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
            UserHandle initiatingUser, Intent originalIntent) {
        boolean isReusedCall = true;
        Call call = reuseOutgoingCall(handle);

        PhoneAccount account =
                mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
        boolean isSelfManaged = account != null && account.isSelfManaged();

        if (call == null) {
            call = new Call(getNextCallId(), mContext,
                    this,
                    mLock,
                    mConnectionServiceRepository,
                    mContactsAsyncHelper,
                    mCallerInfoAsyncQueryFactory,
                    mPhoneNumberUtilsAdapter,
                    handle,
                    null /* gatewayInfo */,
                    null /* connectionManagerPhoneAccount */,
                    null /* phoneAccountHandle */,
                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                    false /* forceAttachToExistingConnection */,
                    false, /* isConference */
                    mClockProxy);
            if ((extras != null) &&
                    extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)) {
                //Reset PostDialDigits with empty string for ConfURI call.
                call.setPostDialDigits("");
            }
            call.initAnalytics();

            call.setIsSelfManaged(isSelfManaged);
            if (isSelfManaged) {
                // Self-managed calls will ALWAYS use voip audio mode.
                call.setIsVoipAudioMode(true);
            }
            call.setInitiatingUser(initiatingUser);
            isReusedCall = false;
        }

        int videoState = VideoProfile.STATE_AUDIO_ONLY;
        if (extras != null) {
            videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            if (VideoProfile.isVideo(videoState)) {
                if (call.isEmergencyCall() && account != null &&
                        !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
                    Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
                            "falling back to audio-only");
                    videoState = VideoProfile.STATE_AUDIO_ONLY;
                } else if (account != null &&
                        !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
                    videoState = VideoProfile.STATE_AUDIO_ONLY;
                }
            }

            call.setVideoState(videoState);
        }

        boolean isAddParticipant = ((extras != null) && (extras.getBoolean(
                TelephonyProperties.ADD_PARTICIPANT_KEY, false)));
        boolean isSkipSchemaOrConfUri = ((extras != null) && (extras.getBoolean(
                TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false) ||
                extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)));

        if (isAddParticipant) {
            String number = handle.getSchemeSpecificPart();
            if (!isSkipSchemaOrConfUri) {
                number = PhoneNumberUtils.stripSeparators(number);
            }
            addParticipant(number);
            mInCallController.bringToForeground(false);
            return null;
        }
        // Force tel scheme for ims conf uri/skip schema calls to avoid selection of sip accounts
        String scheme = (isSkipSchemaOrConfUri? PhoneAccount.SCHEME_TEL: handle.getScheme());

        Log.d(this, "startOutgoingCall :: isAddParticipant=" + isAddParticipant
                + " isSkipSchemaOrConfUri=" + isSkipSchemaOrConfUri + " scheme=" + scheme);

        List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
                phoneAccountHandle, handle, VideoProfile.isVideo(videoState),
                initiatingUser, scheme);
        if (potentialPhoneAccounts.size() == 1) {
            phoneAccountHandle = potentialPhoneAccounts.get(0);
        } else {
            phoneAccountHandle = null;
        }
        call.setTargetPhoneAccount(phoneAccountHandle);

        boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;

        if (!isPotentialInCallMMICode && (!isReusedCall
                && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
            Call foregroundCall = getForegroundCall();
            Log.d(this, "No more room for outgoing call %s ", call);
            if (foregroundCall.isSelfManaged()) {
                //如果正在进行的调用是一个自管理的调用,则提示用户询问是否要断开正在进行的调用并将传出的调用放置。
                call.setOriginalCallIntent(originalIntent);
                startCallConfirmation(call);
            } else {
                // 如果正在进行的呼叫是托管呼叫,我们将阻止传出呼叫拨号
                notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
            }
            return null;
        }

        // The outgoing call can be placed, go forward.

        boolean needsAccountSelection =
                phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
                        && !call.isEmergencyCall() && !isSelfManaged;
        if (needsAccountSelection) {
    
            call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
            extras = new Bundle(extras);
            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
                    potentialPhoneAccounts);
        } else {
            PhoneAccount accountToUse =
                    mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
            if (accountToUse != null && accountToUse.getExtras() != null) {
                if (accountToUse.getExtras()
                        .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
                    Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
                            call.getId());
                    call.setIsVoipAudioMode(true);
                }
            }

            call.setState(
                    CallState.CONNECTING,
                    phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());

            boolean isVoicemail = (call.getHandle() != null)
                    && (PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())
                    || (accountToUse != null && mPhoneAccountRegistrar.isVoiceMailNumber(
                    accountToUse.getAccountHandle(), call.getHandle().getSchemeSpecificPart())));

            if (!isVoicemail && (isRttSettingOn() || (extras != null
                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)))) {
                if (accountToUse != null
                        && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                    call.createRttStreams();
                }
                call.setRequestedToStartWithRtt();
            }
        }
        setIntentExtrasAndStartTime(call, extras);
        // 如果是MMI 号码
        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
            //让CallsManager监听call的行为
            call.addListener(this);
        } else if (!mCalls.contains(call) && mPendingMOEmerCall == null) {
 确保Call不会重复添加(getNewOutgoingCall有重用机制).添加call, 然后callsManager通知监听者,添加了一个call
            addCall(call);
        }

        return call;
    }

 

addCall添加call,然后callsManager通知监听者,添加一个call

 public void addCall(Call call) {
        Trace.beginSection("addCall");
        Log.v(this, "addCall(%s)", call);
        call.addListener(this);// CallsManager 监听 call
        mCalls.add(call); // 添加

        // Specifies the time telecom finished routing the call. This is used by the dialer for
        // analytics.
        Bundle extras = call.getIntentExtras();
        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
                SystemClock.elapsedRealtime());

        updateCanAddCall();
        // onCallAdded for calls which immediately take the foreground (like the first call).onCallAdded用于立即占据前台的调用(如第一次调用)
        for (CallsManagerListener listener : mListeners) {// 告诉所有callsManager的监听者
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.beginSection(listener.getClass().toString() + " addCall");
            }
            listener.onCallAdded(call); //进入监听InCallControoler中
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.endSection();
            }
        }
        Trace.endSection();
    }

接下来需要查看mListeners添加了那些监听,到底是通知了谁,需要查看callsManager中构造构造过程

CallManager的构造函数

 /**
     * Initializes the required Telecom components.
     */
    @VisibleForTesting
    public CallsManager(
            Context context,
            TelecomSystem.SyncRoot lock,
            .....
            ......) {
        mContext = context;
        mLock = lock;
          .....
          ........
          ............

        mDtmfLocalTonePlayer =
                new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
        CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
                context,
                this,
                bluetoothManager,
                wiredHeadsetManager,
                statusBarNotifier,
                audioServiceFactory,
                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
        );

        mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
                mRequester, Looper.getMainLooper());
        mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
        mTtyManager = new TtyManager(context, mWiredHeadsetManager);
        mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
        mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
        mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
        mConnectionServiceRepository =
                new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
        mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
        mClockProxy = clockProxy;
        // 在CallsManager构造的时候, 会给CallsManager添加一系列监听者, 当CallsManager变化时,会通知他们
        mListeners.add(mInCallWakeLockController);
        mListeners.add(statusBarNotifier);
        mListeners.add(mCallLogManager);
        mListeners.add(mPhoneStateBroadcaster);
        //因为目前关注的是InCallController,因为InCallController监听CallsManager,收到来自CallsManager的消息后,
        // 跨进程回到最初的com.android.dialer进程,通知UI发生变化,也就是说InCallController是system进程 主动沟通com.android.dialer进程的桥梁。
        mListeners.add(mInCallController);  // 重点关注 InCallController  liwangjiang
        mListeners.add(mCallAudioManager);
        mListeners.add(mCallRecordingTonePlayer);
        mListeners.add(missedCallNotifier);
        mListeners.add(mHeadsetMediaButton);
        mListeners.add(mProximitySensorManager);

        // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
        final UserManager userManager = UserManager.get(mContext);
        // Don't load missed call if it is run in split user model.
        if (userManager.isPrimaryUser()) {
            onUserSwitch(Process.myUserHandle());
        }
        // Register BroadcastReceiver to handle enhanced call blocking feature related event.
        IntentFilter intentFilter = new IntentFilter(
                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
        intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
        context.registerReceiver(mReceiver, intentFilter);
        QtiCarrierConfigHelper.getInstance().setup(mContext);
    }

目前需要关注的是InCallController,因为InCallControll监听CallsManager,收到来自CallsManager的消息后,跨进程回到最初的com.android.dialer进程,通知UI发生变化,也就是说InCallContrller是system进程主动沟通com.android.dialer进程的桥梁,下面详细解释InCallContoller是如何沟通com.android.dialer进程的。

InCallController.java

我们首先看一下构造函数
InCallController几个内部内的意思:

CarSwappingInCallServiceConnection :  当这两种情况之一发生时,该类实例将接管连接。个版本,他在两个独立子类实例之间切换UI

EmergencyInCallServiceConnection  :  InCallServiceBindingConnection的一个版本,它将所有调用委托给一个辅助连接(CarSwappingInCallServiceConnection),直到它发现一个紧急调用,或者另一个连接终止。当这两种情况之一发生时,该类实例将接管连接。

InCallServiceBindingConnection  最后调用的类 他继承了  InCallServiceConnection
 

 //构造器主要初始化是在TelecomSystem中进行初始化
    public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
            SystemStateProvider systemStateProvider,
            DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
            EmergencyCallHelper emergencyCallHelper) {
        mContext = context;
        mLock = lock;
        mCallsManager = callsManager;
        mSystemStateProvider = systemStateProvider;
        mTimeoutsAdapter = timeoutsAdapter;
        mDefaultDialerCache = defaultDialerCache;
        mEmergencyCallHelper = emergencyCallHelper;

        Resources resources = mContext.getResources();
        mSystemInCallComponentName = new ComponentName( // 这个服务组件时用来控制电话APP的UI的 pag包名  cls类名
                resources.getString(R.string.ui_default_package),//pkg  com.android.dialer
                resources.getString(R.string.incall_default_class));//cls com.android.incallui.InCallServiceImpl

        mSystemStateProvider.addListener(mSystemStateListener);
    }

接下来就是监听的位置了 listener.onCallAdded(call); //进入监听InCallControoler中  InCallControoler是继承CallsManagerListenerBase对象  listener就是CallsManagerListenerBase接口

onCallAdded(call) 方法
 

if (!isBoundAndConnectedToServices()) {
            Log.i(this, "onCallAdded: %s; not bound or connected.", call);
            // 第一次触发时,服务没有绑定,所以需要先绑定服务
            bindToServices(call);
        } else {
           .....
           .......    
        }
    }

bindToServices中主要看if中的 mInCallServiceConnection.connect(call)进入

  public void bindToServices(Call call) {
        if (mInCallServiceConnection == null) {
            InCallServiceConnection dialerInCall = null;
            InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
            Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
            if (defaultDialerComponentInfo != null &&
                    !defaultDialerComponentInfo.getComponentName().equals(
                            mSystemInCallComponentName)) {
                dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
            }
            Log.i(this, "defaultDialer: " + dialerInCall);

            InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
                    mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
            EmergencyInCallServiceConnection systemInCall =
                    new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
            systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());

            InCallServiceConnection carModeInCall = null;
            InCallServiceInfo carModeComponentInfo = getCarModeComponent();
            if (carModeComponentInfo != null &&
                    !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
                carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);//初始化连接
            }

            mInCallServiceConnection =
                    new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
        }

        mInCallServiceConnection.setCarMode(shouldUseCarModeUI());

        // 这个对象,他在两个独立的实例中切换UI
        if (mInCallServiceConnection.connect(call) ==//mInCallServiceConnection.connect(call)开启调用InCallUi
                InCallServiceConnection.CONNECTION_SUCCEEDED) {
            //只有当我们实际连接到主UI InCallServices时,才连接到非UI InCallServices。
            connectToNonUiInCallServices(call);
        } else {
            Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
        }
    }

 

mInCallServiceConnection.connect(call)进入了CartSwapingInCallServiceConnection(内部类)对象中

private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
      @Override
        public int connect(Call call) {
            if (mIsConnected) {
                Log.i(this, "already connected");
                return CONNECTION_SUCCEEDED;
            } else {
                int result = mCurrentConnection.connect(call);  //InCallUi调用  调到EmergencyInCallServiceConnection内部类中
                if (result != CONNECTION_FAILED) {
                    mIsConnected = result == CONNECTION_SUCCEEDED;
                    return result;
                }
            }

            return CONNECTION_FAILED;
        }

}

mCurrentConnection.connect(call);  //InCallUi调用  调到EmergencyInCallServiceConnection内部类中connect函数

/**
*InCallServiceBindingConnection的一个版本,它将所有调用委托给一个辅助连接,
*直到它发现一个紧急调用,或者另一个连接终止。当这两种情况之一发生时,该类实例将接管连接。
*/
private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
        @Override
        public int connect(Call call) {
            mIsConnected = true;
            if (mIsProxying) {
                int result = mSubConnection.connect(call);//InCallUi调用
                mIsConnected = result == CONNECTION_SUCCEEDED;
                if (result != CONNECTION_FAILED) {
                    return result;
                }
                // Could not connect to child, stop proxying.
                mIsProxying = false;
            }

            mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
                mCallsManager.getCurrentUserHandle());

            if (call != null && call.isIncoming()
                && mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
              // Add the last emergency call time to the call
              Bundle extras = new Bundle();
              extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
                      mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
              call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
            }

            //调用这里,连接父接口中的connect()方法。
            return super.connect(call);
        }
}

EmergencyInCallServiceConnection继承了InCallServiceBindingConnection 所以调用super.connect就是调用了InCallServiceBindingConnection里面的 connect()方法

InCallServiceBindingConnection中的connect方法

private class InCallServiceBindingConnection extends InCallServiceConnection {
@Override
        public int connect(Call call) {
            if (mIsConnected) {
                Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request.");
                return CONNECTION_SUCCEEDED;
            }

            if (call != null && call.isSelfManaged() &&
                    !mInCallServiceInfo.isSelfManagedCallsSupported()) {
                Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
                        mInCallServiceInfo);
                mIsConnected = false;
                return CONNECTION_NOT_SUPPORTED;
            }
            //开启InCallService
            Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
            intent.setComponent(mInCallServiceInfo.getComponentName());
            if (call != null && !call.isIncoming() && !call.isExternalCall()){
                intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
                        call.getIntentExtras());
                intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                        call.getTargetPhoneAccount());
            }

            Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
            mIsConnected = true;
            if (!mContext.bindServiceAsUser(intent, mServiceConnection,   //开启服务  调用InCallServiceImpl
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
                        Context.BIND_ABOVE_CLIENT,
                        UserHandle.CURRENT)) {
                Log.w(this, "Failed to connect.");
                mIsConnected = false;
            }

            if (call != null && mIsConnected) {
                call.getAnalytics().addInCallService(
                        mInCallServiceInfo.getComponentName().flattenToShortString(),
                        mInCallServiceInfo.getType());
            }

            return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
        }
}

这里已经开启服务,开启了Dialer中InCallUi,在InCallUi中有一个服务InCallServiceImpl,这个服务就是开启dialer里面的InCallUi的入口
调用完毕后回调到InCallController中的InCallServiceBindingConnection中的ServiceConnection里面的onServiceConnected()方法  ,

  @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.startSession("ICSBC.oSC");
                synchronized (mLock) {
                    try {
                        Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                        mIsBound = true;
                        if (mIsConnected) {
                            //  成功后调用InCallServer ,连接成功往下走
                            onConnected(service);
                        }
                    } finally {
                        Log.endSession();
                    }
                }
            }

绑定与InCallUi数据传输方法

  protected void onConnected(IBinder service) {
            boolean shouldRemainConnected =
                    InCallController.this.onConnected(mInCallServiceInfo, service);//往下执行
            if (!shouldRemainConnected) {
                // Sometimes we can opt to disconnect for certain reasons, like if the
                // InCallService rejected our initialization step, or the calls went away
                // in the time it took us to bind to the InCallService. In such cases, we go
                // ahead and disconnect ourselves.
                disconnect();
            }
        }


private boolean onConnected(InCallServiceInfo info, IBinder service) {
        Trace.beginSection("onConnected: " + info.getComponentName());
        Log.i(this, "onConnected to %s", info.getComponentName());

        IInCallService inCallService = IInCallService.Stub.asInterface(service);
        mInCallServices.put(info, inCallService);

        try {
            // 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
            // InCallAdapter 是一个 Binder。
            // 需要对Android 通过 Binder实现跨进程调用有一定了解
            // 还有对Android 的AIDL由一定了解
            inCallService.setInCallAdapter(
                    new InCallAdapter(
                            mCallsManager,
                            mCallIdMapper,
                            mLock,
                            info.getComponentName().getPackageName()));
        } catch (RemoteException e) {
            Log.e(this, e, "Failed to set the in-call adapter.");
            Trace.endSection();
            return false;
        }

        // Upon successful connection, send the state of the world to the service.
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        for (Call call : calls) {
            try {
                if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
                        (call.isExternalCall() && !info.isExternalCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());

                // Track the call if we don't already know about it.
                addCall(call);
                numCallsSent += 1;
                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
                        call,
                        true /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        includeRttCall));
            } catch (RemoteException ignored) {
            }
        }
        try {
            inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
            inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
        } catch (RemoteException ignored) {
        }
        Log.i(this, "%s calls sent to InCallService.", numCallsSent);
        Trace.endSection();
        return true;
    }

InCallController 内部类的逻辑   第一先是调用 CarSwappingInCallServiceConnection该内部类的作用是管理连接其他两个子类(EmergencyInCallServiceConnection   ,InCallServiceBindingConnection)之间互相切换,

交互图:

 

 

com.android.dialer进程

InCallServiceImpl InCallService的实现类,负责和Telecom沟通,更新

InCallServiceImp的核心是在的他父类InCallService,InCallServiceImpl绑定时返回一个InCallServiceBinder,因此system进程拿到的接口就到的接口就是这个,绑定之前会初始化InCallPresenter,InCallPresenter是全局唯一(单例)的,他负责更新APP的UI

InCallServiceImp.java

@Override
  public IBinder onBind(Intent intent) {
    final Context context = getApplicationContext();
	
    getResources().updateConfiguration(configuration,displayMetrics);


    QtiCarrierConfigHelper.getInstance().setup(context);
    final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);

    InCallPresenter.getInstance()
        .setUp(
            context,
            CallList.getInstance(),
            new ExternalCallList(),
            new StatusBarNotifier(context, contactInfoCache),
            new ExternalCallNotifier(context, contactInfoCache),
            contactInfoCache,
            new ProximitySensor(
                context, AudioModeProvider.getInstance(), new AccelerometerListener(context)),
            new FilteredNumberAsyncQueryHandler(context));
    InCallPresenter.getInstance().onServiceBind();
    // 核心, 主过程
    InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
    TelecomAdapter.getInstance().setInCallService(this);


    InCallWindow.getInstance(context).initInCallWindow();
    ImmersionInCallDialogMgr.getInstance().init(context, contactInfoCache);

    // 返回一个 InCallServiceBinder
    return super.onBind(intent);
  }

InCallPresenter MVC模式中,控制逻辑和UI

InCallPreseter的maybeStartRevealAnimatin会启动InCallActivity,即拨号进入中的界面(或者是来电界面)

public void maybeStartRevealAnimation(Intent intent) {
    if (intent == null || mInCallActivity != null) {
      return;
    }
    final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
    if (extras == null) {
      // Incoming call, just show the in-call UI directly.
      return;
    }

    if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
      // Account selection dialog will show up so don't show the animation.
      return;
    }

    final PhoneAccountHandle accountHandle =
        intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
    final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);

    InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);

    final Intent activityIntent =
        InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
    activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
    mContext.startActivity(activityIntent);//启动InCallActivity
  }

Dialer启动图:

 

调起InCallUi后就是更新InCallUi数据了。

回到刚刚开启服务的位置 mContext.bindServiceAsUser(intent, mServiceConnection,.....) 这里传入了mServiceConnection,InCallServiceImpl连接成功后回调到mServiceConnection, 

InCallController.java

private final ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.startSession("ICSBC.oSC");
                synchronized (mLock) {
                    try {
                        Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                        mIsBound = true;
                        if (mIsConnected) {//是否连接
                            // 只有当我们被认为是有联系的时候才继续。 成功后调用InCallServer
                            onConnected(service);
                        }
                    } finally {
                        Log.endSession();
                    }
                }
            }
}
protected void onConnected(IBinder service) {
            boolean shouldRemainConnected =
                    InCallController.this.onConnected(mInCallServiceInfo, service);//往下执行
            if (!shouldRemainConnected) {
                disconnect();
            }
        }

onConnected这里面设置了很多监听,通过IInCallService调用到了framework/base/telecom  InCallService中进行处理.

  private boolean onConnected(InCallServiceInfo info, IBinder service) {
        Trace.beginSection("onConnected: " + info.getComponentName());
        Log.i(this, "onConnected to %s", info.getComponentName());
        //进入Framework/base/Telecom
        IInCallService inCallService = IInCallService.Stub.asInterface(service);
        mInCallServices.put(info, inCallService);

        try {
            // 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
            // InCallAdapter 是一个 Binder。
            // 需要对Android 通过 Binder实现跨进程调用有一定了解
            // 还有对Android 的AIDL由一定了解  调用到了Framework/base/telecom  InCallServce
            inCallService.setInCallAdapter(
                    new InCallAdapter(
                            mCallsManager,
                            mCallIdMapper,
                            mLock,
                            info.getComponentName().getPackageName()));
        } catch (RemoteException e) {
            Log.e(this, e, "Failed to set the in-call adapter.");
            Trace.endSection();
            return false;
        }

        // Upon successful connection, send the state of the world to the service.成功连接后,将 world 状态发送到服务。
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        for (Call call : calls) {
            try {
                if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
                        (call.isExternalCall() && !info.isExternalCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());

                // Track the call if we don't already know about it.
                addCall(call);
                numCallsSent += 1;
                //添加调用
                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
                        call,
                        true /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        includeRttCall));
            } catch (RemoteException ignored) {
            }
        }
        try {
            inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
            inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
        } catch (RemoteException ignored) {
        }
        Log.i(this, "%s calls sent to InCallService.", numCallsSent);
        Trace.endSection();
        return true;
    }

我们先看inCallService.setInCallAdapter方法   他进入的是framework/base/telecom InCallService.java , 

// 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
// InCallAdapter 是一个 Binder。
// 需要对Android 通过 Binder实现跨进程调用有一定了解
// 还有对Android 的AIDL由一定了解  调用到了Framework/base/telecom  InCallServce
 private final class InCallServiceBinder extends IInCallService.Stub {
        @Override
        public void setInCallAdapter(IInCallAdapter inCallAdapter) {
            mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
        }

        @Override
        public void addCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
        }

        @Override
        public void updateCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
        }
        忽略......
 }

进入Handler

  /** Default Handler used to consolidate binder method calls onto a single thread. */
    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
                return;
            }

            switch (msg.what) {
                case MSG_SET_IN_CALL_ADAPTER:
                    String callingPackage = getApplicationContext().getOpPackageName();
                    // setInCallAdapter 触发创建 com.android.dialer 的唯一 的phone对象
                    // 也就是说, 在 com.android.dialer 进程的所有操作 都是经过 phone 里面的mInCallAdapter
                    // 发送到 system 进程的 (框架层)
                    mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
                            getApplicationContext().getApplicationInfo().targetSdkVersion);
                    // 核心, 让InCallService 监听 Phone 的状态变化
                    mPhone.addListener(mPhoneListener);
                    onPhoneCreated(mPhone);
                    break;
                case MSG_ADD_CALL:  //添加Call时调用
                    mPhone.internalAddCall((ParcelableCall) msg.obj);
                    break;
                case MSG_UPDATE_CALL:
                    mPhone.internalUpdateCall((ParcelableCall) msg.obj);
                    break;
                case MSG_SET_POST_DIAL_WAIT: {
                    SomeArgs args = (SomeArgs) msg.obj;
                ........
                忽略大量消息类型

这里mPhone.addListener(mPhoneLintener)  他添加到Phone中等待响应

然后是调用 inCallService.addCall()方法

进入了Handler 中的  case MSG_ADD_CALL:  

调用mPhone.InternalAddCall();进入了mPhone对象

Phone.java   

 final void internalAddCall(ParcelableCall parcelableCall) {
        Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
        mCallByTelecomCallId.put(parcelableCall.getId(), call);
        mCalls.add(call);
        checkCallTree(parcelableCall);
        call.internalUpdate(parcelableCall, mCallByTelecomCallId);
        fireCallAdded(call);//进入回调
     }

这里只是处理一些Call的处理

 private void fireCallAdded(Call call) {
        for (Listener listener : mListeners) {
            listener.onCallAdded(this, call);
        }
    }

mListeners是mPhone.addListener(mPhoneLintener)添加进来的,调用onCallAdded() 方法,回调到 InCallService里面的private Phone.Listener mPhoneListener = new Phone.Listener(){ }中

  private Phone.Listener mPhoneListener = new Phone.Listener() {
   省略其他回调.....
      /** ${inheritDoc} */
        @Override
        public void onCallAdded(Phone phone, Call call) {
            InCallService.this.onCallAdded(call); //onCallAded本类里面没有操作,操作子类实现
        }
     省略其他回调.....     
  }

这里的InCallServie.this.onCallAdded()方法提供给子类实现  状态更新  传入Call对象

我们在来看一下他的子类  com.android.Dialer进程

InCallServiceImpl.java

  @Override
  public void onCallAdded(Call call) {
    InCallPresenter.getInstance().onCallAdded(call);
  }

这里就是更新状态入口

InCallPresenter的作用:从CallList获取更新,并将更改通知InCallActivity (UI)。负责启动新调用的活动,并在所有调用都断开时完成该活动。创建和管理调用内状态,并为希望在调用内状态更改时侦听的演示程序提供侦听器模式。

InCallPresenter.java

public void onCallAdded(final android.telecom.Call call) {
    LatencyReport latencyReport = new LatencyReport(call);

    if (shouldAttemptBlocking(call)) {
      maybeBlockCall(call, latencyReport);
    } else {
      if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
        mExternalCallList.onCallAdded(call);
      } else {
        latencyReport.onCallBlockingDone();
        mCallList.onCallAdded(mContext, call, latencyReport);  //核心主过程
      }
    }

    // Since a call has been added we are no longer waiting for Telecom to send us a call. 
   由于增加了一个呼叫,我们不再等待电信给我们发送一个呼叫
    setBoundAndWaitingForOutgoingCall(false, null);
    call.registerCallback(mCallCallback);
  }

进入CallList进行call对象的数据解析和加载,
CallList.java

  public void onCallAdded(
      final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
    省略......
    .........
    if (call.getState() == DialerCall.State.INCOMING
        || call.getState() == DialerCall.State.CALL_WAITING) {
      onIncoming(call);  //主核心
    } else {
      dialerCallListener.onDialerCallUpdate();
    }

    if (call.getState() != State.INCOMING) {
      // Only report outgoing calls
      ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
    }

    Trace.endSection();
  }
  private void onIncoming(DialerCall call) {

    for (Listener listener : mListeners) {
      listener.onIncomingCall(call);//回调进入InCallPresenter
    }
  }

这里的mListeners是InCallPresenter传入this,InCallPresenter实现了Listener 

InCallPresenter.java

 /** Called when there is a new incoming call. */
  @Override
  public void onIncomingCall(DialerCall call) {

    InCallState newState = startOrFinishUi(InCallState.INCOMING);
    InCallState oldState = mInCallState;
    registerFoldAnswer();
    mInCallState = newState;
    for (IncomingCallListener listener : mIncomingCallListeners) {
      listener.onIncomingCall(oldState, mInCallState, call);
    }
    if (mInCallActivity != null) {
      // Re-evaluate which fragment is being shown.
      mInCallActivity.onPrimaryCallStateChanged();//更新Activity 状态
    }
  }

流程图:

 

接下来我们分析一下Log是怎么调用InCallUi的

//Dialer拨号  Dialer进程
09-17 21:29:31.099 I/Dialer  (28149): CallLogFragment - callBack()
09-17 21:29:31.103 I/Dialer  (28149): PerformanceReport.stopRecording - enter

//phone进程  也就是package/service/telecom
UserCallIntentProcessor.java中准备跳转意图,跳转进入PrimaryCallReceiver
09-17 21:29:31.123 I/Telecom ( 1141): UserCallIntentProcessor: sendIntentToDestination: send intent to Telecom directly.: TSI.pC@A4U

通过PrimaryCallReceiver.java这个广播进入了CallIntentProcessor.java  processIntent方法中,isUnknownCall: false: 是不是陌生电话
09-17 21:29:31.124 I/Telecom ( 1141): CallIntentProcessor: onReceive - isUnknownCall: false: TSI.pC@A4U

进入InCallController.java中的onCallAdded方法通过CallsManager进行调用 listener.onCallAdded(call);
09-17 21:29:31.149 I/Telecom ( 1141): InCallController: onCallAdded: [TC@3, CONNECTING, null, tel:***********, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]; not bound or connected.: TSI.pC@A4U

进入InCallController.java bindToServices 方法
09-17 21:29:31.150 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U
09-17 21:29:31.151 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U

EmergencyInCallServiceConnection是InCallController 的内部类,这里开始连接InCallService,最终连接的是InCallServiceImpl
09-17 21:29:31.152 I/Telecom ( 1141): EmergencyInCallServiceConnection: Attempting to bind to InCall [ComponentInfo{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl} supportsExternal? true supportsSelfMg?false], with Intent { act=android.telecom.InCallService cmp=org.codeaurora.dialer/com.android.incallui.InCallServiceImpl (has extras) }: TSI.pC@A4U


//Dialer进程
进入Dialer 里面的InCallServiceImpl.java 里面的onBind() 方法  oldLock 是否是老的InCallPresenter   ,这里的log是开发自己增加的 ...
09-17 21:29:31.178 I/Dialer  (28149): InCallServiceImpl.onBind - oldLock = 0


//phone进程
与InCallServiceImpl连接成功   info,返回的InCallSerivceInfo
09-17 21:29:31.281 I/Telecom ( 1141): InCallController: onConnected to Component Info{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl}: ICSBC.oSC@A4Y

======================
这一步已经调起了InCallUi  只需要等待数据更新
======================
连接后会进入framework/base中InCallService中的内部的InCallServiceBinder内部类中进行处理,InCallServiceBinder他里面的方法就是给Handler发送信息
Handler调用的方法传输数据给子类进行处理。

//Dialer进程  InCallSerivce只有 Dialer进程中的InCallServiceImpl实现过  InCallServiceImpl实现了InCallService中的一些方法比如onCallAdded()、
加载页面
09-17 21:29:31.614 I/Dialer  (28149): InCallFragment.setCallState - PrimaryCallState, state: 13, connectionLabel: 
09-17 21:29:31.615 I/Dialer  (28149): ContactGridManager - setCallState: 13
09-17 21:29:31.616 I/Dialer  (28149): InCallFragment.setContactGridViewMarginTop - screenMode: 0  screenOrientation: 1
InCallActivity数据更新
09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.onPrimaryCallStateChanged
09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.showMainInCallFragment - shouldShowAnswerUi: false, shouldShowVideoUi: false, didShowAnswerScreen: false, didShowInCallScreen: true, didShowVideoCallScreen: false
09-17 21:29:31.616 I/Dialer  (28149): InCallFragment.onInCallShowDialpad - isShown: false

总结一下: system 进程的 InCallController 收到来自于 CallsManager 的消息后, 发送到 com.android.dialer 进程, 一步步地更新 拨号界面的状态, 整个过程已经全部打通了,只需要调用CallsManager中的addCall方法就会更新状态。

 

结语:我语文没及格过,凑活着看吧。代码挺长,如果有错误请提出做 更正,如有内容在加,谢谢观看 ,如果满意点个赞吧 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章