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 

 

流程图:

Phone拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)_第1张图片

 

 

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

概述:拨号调用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拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)_第2张图片

 

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 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 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 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)之间互相切换,

交互图:

 

Phone拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)_第3张图片

 

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启动图:

Phone拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)_第4张图片

 

调起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 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 状态    }  }

流程图:

Phone拨号调起InCallUi流程(Phone 9.0 )(Phone调用InCallUi流程)_第5张图片

 

接下来我们分析一下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/telecomUserCallIntentProcessor.java中准备跳转意图,跳转进入PrimaryCallReceiver09-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@A4U09-17 21:29:31.151 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4UEmergencyInCallServiceConnection是InCallController 的内部类,这里开始连接InCallService,最终连接的是InCallServiceImpl09-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,返回的InCallSerivceInfo09-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: 1309-17 21:29:31.616 I/Dialer  (28149): InCallFragment.setContactGridViewMarginTop - screenMode: 0  screenOrientation: 1InCallActivity数据更新09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.onPrimaryCallStateChanged09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.showMainInCallFragment - shouldShowAnswerUi: false, shouldShowVideoUi: false, didShowAnswerScreen: false, didShowInCallScreen: true, didShowVideoCallScreen: false09-17 21:29:31.616 I/Dialer  (28149): InCallFragment.onInCallShowDialpad - isShown: false

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

 

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

更多相关文章

  1. 使用AIDL实现Android的跨进程通信
  2. android跨进程通信(IPC):使用AIDL
  3. View类xml属性、方法
  4. 【Android 开发】:UI控件之显示图片控件 ImageView 的使用方法
  5. Android与JavaScript方法相互调用!
  6. TabLayout绑定Viewpager后不显示文字
  7. Android 系统音量最大值的定义位置以及默认值的修改方法

随机推荐

  1. android studio AndroidManifest.xml命名
  2. Android(安卓)Universal Image Loader 源
  3. Android布局属性详解
  4. 编译代码报出Android(安卓)library proje
  5. android触屏手势识别全解析
  6. 模仿天天动听的seekbar
  7. Android(安卓)关于WebView的相关属性
  8. Android(安卓)获取系统权限的代码
  9. Android(安卓)Studio中点击按钮跳转到其
  10. [Android] Android(安卓)使用 Greendao