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个模块。
- com.android.dialer进程,电话APP模块 路径 : android/vendor/codeaurora/commonsys/packages/apps/Dialer
- com.android.dialer.InCallUi 呼出呼入页面 路径 :要看厂商是否分离了,如果是高通代码就放在Dialer App中
- System进程,系统框架层 Service/telecom层 路径 :android/packages/service
- com.android.phone进程, framework/ : 路径 : android/framework
拨号过程由App开始(或第三方调用Telephony),进入接口System进程获取接口,System接口会phone的一个Binder中的一个方法进行处理,进入Phone后做一些判断(比如:号码是否正确), 在启动InCallUil设置状态 ,状态改变 是通过Call对象进行变更
在电话中java层有三个Call对象 所在位置是
- 路径:android/packages/services/Telecomm
- 路径:android/frameworks/base/telecomm
- 路径: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 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)之间互相切换,
交互图:
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 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/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方法就会更新状态。
结语:我语文没及格过,凑活着看吧。代码挺长,如果有错误请提出做 更正,如有内容在加,谢谢观看 ,如果满意点个赞吧 。
更多相关文章
- 使用AIDL实现Android的跨进程通信
- android跨进程通信(IPC):使用AIDL
- View类xml属性、方法
- 【Android 开发】:UI控件之显示图片控件 ImageView 的使用方法
- Android与JavaScript方法相互调用!
- TabLayout绑定Viewpager后不显示文字
- Android 系统音量最大值的定义位置以及默认值的修改方法