电话应用框架

Android电话模块是一个典型的分层结构设计,如下:

电话框架分为4个层次,分别为:应用层、框架层(framework层,简称fw)、RIL(Radio Interface Layer)、modem。

应用层:app应用,包括Dialer.apk、TeleService.apk、Telecom.apk、InCallUI.apk。

其中Dialer.apk跑在com.android.dialer进程中,TeleService.apk跑在常驻进程com.android.phone进程中,Telecom.apk跑在system进程中、InCallUI.apk跑在com.android.incallui进程中。

框架层:包括telephony fw、telecom fw。Code分别位于frameworks/opt/telephony、frameworks/base/telecomm。

RIL:位于User Libraries层中的HAL层,提供AP(Application Processor,应用处理器)和BP(Baseband Processor,基带处理器)之间的通信功能。RIL通常分为RILJ、RILC,RILJ即为java的RIL.java,code位于框架层,RILC才是真正的RIL层。Android的RIL驱动模块,在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so三个部分。

Modem:位于BP,负责实际的无线通信能力处理

实现各进程交互的aidl:

拨号流程框架流程

  • packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadFragment.java

用户点击拨号盘的拨号按钮,此时开始呼叫长征第一步,dialpadfragment的onclick方法会响应点击事件。

  @Overridepublic void onClick(View view) {int resId = view.getId();if (resId == R.id.dialpad_floating_action_button) {view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);handleDialButtonPressed();}

找到拨号界面对应的l ayout 界面布局文件dialer/dialpadview/res/layout/dialpad_fragment.xml ,其中就包含了打开拨号盘的浮动按钮,其定义如下

在该类中 运行:handleDialButtonPressed(); 方法,判断是否号码是否为空,是否非法等情况

  • dialer/precall/PreCall.java

10 之前没有这一步,直接调用 DialerUtils.startActivityWithErrorToast() 方法

  • dialer/util/DialerUtils.java

携带Intent.ACTION_CALL的Intent Action会走到TelecomUtil placeCall流程,否则直接context.startActivity(intent);

    public static void startActivityWithErrorToast(final Context context, final Intent intent, int msgId) {try {if ((Intent.ACTION_CALL.equals(intent.getAction()))) {placeCallOrMakeToast(context, intent);} else {context.startActivity(intent);

startActivityWithErrorToast() 方法 调用下列方法

hasCallPhonePermission()会检查是否有呼叫权限,是否有Manifest.permission.CALL_PHONE)权限:

    private static void placeCallOrMakeToast(Context context, Intent intent) {final boolean hasCallPermission = TelecomUtil.placeCall(context, intent);if (!hasCallPermission) {Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT).show();}}
  • dialer/telecom/TelecomUtil.java

 public static boolean placeCall(Context context, Intent intent) {if (hasCallPhonePermission(context)) {getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());return true;}return false;}

TelecomManager.placeCall主要获取TelecomService的Binder接口,跨进程进入到TelecomService(system进程)内部,至此 com.android.dialer 进程的工作暂时完成

  private static TelecomManager getTelecomManager(Context context) {return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);}
  • telecom/TelecomManager.java

Telecom Manager 获取ITelecomService 服务并调用其placeCall 方法继续传递intent 发出通话呼叫请求,将涉及第一次跨进程的服务调用。

    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());} catch (RemoteException e) {Log.e(TAG, "Error calling ITelecomService#placeCall", e);}}}

其中,getTelecomService() 方法

    private ITelecomService getTelecomService() {if (mTelecomServiceOverride != null) {return mTelecomServiceOverride;}return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));}

调用ITelecomService的placeCall方法,此处是aidl调用,对应的我们需要找到接收的地方,按照命名规则应该是TelecomServiceImpl:

  • server/telecom/TelecomServiceImpl.java

创建UserCallIntentProcessorFactory,调用processIntent方法处理呼叫:

private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {@Overridepublic void placeCall(Uri handle, Bundle extras, String callingPackage) {try {synchronized (mLock) {final UserHandle userHandle = Binder.getCallingUserHandle();long token = Binder.clearCallingIdentity();try {final Intent intent = new Intent(Intent.ACTION_CALL, handle);mUserCallIntentProcessorFactory.create(mContext, userHandle).processIntent(intent, callingPackage, hasCallAppOp && hasCallPermission);}}} }
}
  • server/telecom/components/UserCallIntentProcessor.java

processIntent判断是否是呼叫请求:

    public void processIntent(Intent intent, String callingPackageName,boolean canCallNonEmergency, boolean isLocalInvocation) {// Ensure call intents are not processed on devices that are not capable of calling.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);}}
  • Intent.ACTION_CALL: 可以拨打普通呼叫
public static final String ACTION_CALL = "android.intent.action.CALL";
  • Intent.ACTION_CALL_PRIVILEGED:可以拨打任意类型号码
public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
  • Intent.ACTION_CALL_EMERGENCY:可以拨打紧急呼叫
public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";

processOutgoingCallIntent方法:

private void processOutgoingCallIntent(Intent intent, String callingPackageName,boolean canCallNonEmergency, boolean isLocalInvocation) {------------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.intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);sendIntentToDestination(intent, isLocalInvocation, callingPackageName);}

sendIntentToDestination 方法:

    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,String callingPackage) {intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);if (isLocalInvocation) {// We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't// bother trampolining the intent, just sent it directly to the call intent processor.// TODO: We should not be using an intent here; this whole flows needs cleanup.Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");synchronized (TelecomSystem.getInstance().getLock()) {TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,callingPackage);}} else {// We're calling from the UserCallActivity, so the TelecomSystem is not in the same// process; we need to trampoline to TelecomSystem in the system server process.Log.i(this, "sendIntentToDestination: trampoline to Telecom.");TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);tm.handleCallIntent(intent);}return true;}

安卓10 这里去除了以前的广播机制

调用getTelecomSystem方法返回TelecomSystem对象,调用getCallIntentProcessor()返回CallIntentProcessor对象,然后调用processIntent方法

  • server/telecom/CallIntentProcessor.java

    public void processIntent(Intent intent, String callingPackage) {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, callingPackage);}Trace.endSection();}

processOutgoingCallIntent方法,调用CallsManager的startOutgoingCall()方法创建Call,后new NewOutgoingCallIntentBroadcaster,调用processIntent()方法:

CallsManager 创建Call, 链接,监听Call状态

下面先分析  startOutgoingCall()方法, 

再分析 new NewOutgoingCallIntentBroadcaster,的processIntent()方法

    static void processOutgoingCallIntent(Context context,CallsManager callsManager,Intent intent,String callingPackage) {--------// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns// 先执行此处代码,更新 ui 界面CompletableFuture<Call> callFuture = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,intent, callingPackage);final Session logSubsession = Log.createSubsession();callFuture.thenAccept((call) -> {if (call != null) {Log.continueSession(logSubsession, "CIP.sNOCI");try {// 实际的,将上层信息下发到telephony, rilsendNewOutgoingCallIntent(context, call, callsManager, intent);} finally {Log.endSession();}}});}

其中的 sendNewOutgoingCallIntent 方法如下定义:‘

    static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,Intent intent) {// Asynchronous calls should not usually be made inside a BroadcastReceiver because once// onReceive is complete, the BroadcastReceiver's process runs the risk of getting// killed if memory is scarce. However, this is OK here because the entire Telecom// process will be running throughout the duration of the phone call and should never// be killed.final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),isPrivilegedDialer);// If the broadcaster comes back with an immediate error, disconnect and show a dialog.NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {disconnectCallAndShowErrorDialog(context, call, disposition.disconnectCause);return;}broadcaster.processCall(disposition);}
  • services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

 public void processCall(CallDisposition disposition) {if (disposition.callImmediately) {boolean speakerphoneOn = mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);int videoState = mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,VideoProfile.STATE_AUDIO_ONLY);placeOutgoingCallImmediately(mCall, disposition.callingAddress, null,speakerphoneOn, videoState);// Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast// so that third parties can still inspect (but not intercept) the outgoing call. When// the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to// initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.}

其中 placeOutgoingCallImmediately 代码:

    private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,boolean speakerphoneOn, int videoState) {Log.i(this,"Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");// Since we are not going to go through "Outgoing call broadcast", make sure// we mark it as ready.mCall.setNewOutgoingCallIntentBroadcastIsDone();mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);}

先执行了 startOutgoingCall 方法 ,然后再 执行 broadcaster. processCall () 方法 

备注:

这里先设置一个分支,先分析 callsManager.startOutgoingCall 方法

CallsManager 的拨号处理流程

  • server/telecom/CallsManager.java

CallsManager.statOutgoingCall 的主要逻辑是创建、更新和保存Call 对象

startOutgoingCall 新建一个(或者重用) call, 将CallsManager绑定监听该 call, 然后通知 callsManager 的监听者, 添加了一个 call

Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {Call call = getNewOutgoingCall(handle);// 如果是MMI 号码if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {// 让CallsManager监听call的行为call.addListener(this);} else if (!mCalls.contains(call)) {// 确保Call不会重复添加(getNewOutgoingCall有重用机制)// 添加call, 然后callsManager通知监听者,添加了一个calladdCall(call);}return call;}

addCall 方法:addCall 添加call, 然后callsManager通知监听者,添加了一个call

CallsManager对象将保存多个Call 对象到mCalls 集合中, Call 对象则设置Listener 对象为
CallsManager , 对象之间相互引用。而CallsManager 对象通过 mlisteners  发出onCallAdded 消息
回调。那么mlisteners 究竟是什么呢?摘录出CallsManager类的属性定义和构造方法中的关键逻
辑,

CallsManager 的构造函数

CallsManager(Context context,TelecomSystem.SyncRoot lock,ContactsAsyncHelper contactsAsyncHelper,CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,MissedCallNotifier missedCallNotifier,PhoneAccountRegistrar phoneAccountRegistrar,HeadsetMediaButtonFactory headsetMediaButtonFactory,ProximitySensorManagerFactory proximitySensorManagerFactory,InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {// ....// 在CallsManager构造的时候, 会给CallsManager添加一系列监听者, 当CallsManager变化时,会通知他们mListeners.add(statusBarNotifier);mListeners.add(mCallLogManager);mListeners.add(mPhoneStateBroadcaster);// 重点关注 InCallController, log中会打印mListeners.add(mInCallController);mListeners.add(mRinger);mListeners.add(new RingbackPlayer(this, playerFactory));mListeners.add(new InCallToneMonitor(playerFactory, this));
// 监听手机是否是蓝牙、有限耳机、听筒、扬声器模式mListeners.add(mCallAudioManager);mListeners.add(missedCallNotifier);
// 语音提示声mListeners.add(mDtmfLocalTonePlayer);mListeners.add(mHeadsetMediaButton);mListeners.add(mProximitySensorManager);// ...
}

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

  • server/telecom/InCallController.java

InCallController 用于控制电话App的逻辑和UI

lnCallController. onCallAdded 消息回调

    public void onCallAdded(Call call) {// 先判断是否绑定了服务,第一次的话先绑定if (!isBoundAndConnectedToServices()) {Log.i(this, "onCallAdded: %s; not bound or connected.", call);// We are not bound, or we're not connected.bindToServices(call); // 绑定服务} else {// We are bound, and we are connected.adjustServiceBindingsForEmergency();

其中的   bindToService() 方法:

    @VisibleForTestingpublic 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)) {
// 重点关注创建的InCallServiceBindingConnection 对象
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());// Actually try binding to the UI InCallService.  If the response
//执行绑定通话界面InCa ll Service 服务的真实操作,判断返回结果if (mInCallServiceConnection.connect(call) ==InCallServiceConnection.CONNECTION_SUCCEEDED) {// Only connect to the non-ui InCallServices if we actually connected to the main UI// one.
//只有当我们成功连接到通话界面服务后, 才执行连接无界面的IncallService 服务connectToNonUiInCallServices(call);mBindingFuture = new CompletableFuture<Boolean>().completeOnTimeout(false,mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(mContext.getContentResolver()),TimeUnit.MILLISECONDS);} else {Log.i(this, "bindToServices: current UI doesn't support call; not binding.");}}

上述的类中,InCalIServiceConnection 作为lnCallController的内部公有类,

lnCallServiceBindingConnection 、EmergencylnCallServiceConnection 和CarSwappinglnCallServiceConnection这三个类作为lnCallController的内部私有类,

全部继承于lnCallServiceConnection 类。

mInCallServiceConnection.connect(call) 实际调用的是 lnCallServiceBindingConnection 对象的connect 方法,

        @Overridepublic 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;}// 指明了绑定In CallService . SERVICE_INTERFACE 服务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,Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,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;}

上述代码中存在一个  mServiceceConnection对象

看一下 ServiceConnection mServiceceConnection 对象

        private final ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic 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) {// Only proceed if we are supposed to be connected.onConnected(service);}} finally {Log.endSession();}}}

onConnected 方法:其中又有一个onConnected 方法

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

上面的代码逻辑体现了绑定服务的全部过程。首先,创建lnCallServiceBindingConnection 对象,创建该对象的同时将同步创建一个mServiceConnection 对象,此对象为匿名的ServiceConnection 类型,重写了onServiceConnected 和onServiceDisconnected 方法

;接着,创建action 为lnCallService. SERVICEINTERFACE 的intent 对象,并更新了PhoneAccount 和Call 的一些关键信息;然后,调用Android 系统的bindServiceAsUser 方法绑定服务;最后是绑定服务成功以后的收尾工作--- onConnected 系统回调,

最后,将发起对  InCallController.this.onConnected  的调用, 方法中的主要处理逻辑详情如下:

    private boolean onConnected(InCallServiceInfo info, IBinder service) {Trace.beginSection("onConnected: " + info.getComponentName());Log.i(this, "onConnected to %s", info.getComponentName());// 获取IInCallService 服务并保存IInCallService inCallService = IInCallService.Stub.asInterface(service);mInCallServices.put(info, inCallService);try {
//调用服务方法增加IAdapter, InCallAdapter 实现了IInCallAdapter.aidl 接口inCallService.setInCallAdapter(new InCallAdapter(mCallsManager,mCallIdMapper,mLock,info.getComponentName().getPackageName()));} catch (RemoteException e) {Log.e(this, e, "Failed to set the in-call adapter.");Trace.endSection();return false;}// Upon successful connection, send the state of the world to the service.List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +"calls", calls.size(), info.getComponentName());int numCallsSent = 0;
//将之前保存的Call 对象通过inCallService 发送出去for (Call call : calls) {try {//注意这里有Call 对象的转换, 将com.android.server.telecom.Call 转换为可跨进
// 程传递的对象android.telecom.ParcelableCallparcelableCall = ParcelableCallUtils.toParcelableCall(call,true /* includeVideoProvider */,mCallsManager.getPhoneAccountRegistrar(),info.isExternalCallsSupported(),includeRttCall,info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);inCallService.addCall(parcelableCall);// Track the call if we don't already know about it.addCall(call);} catch (RemoteException ignored) {Log.e(this, ignored, "add call fialed");}}try {
// 通知Call 相关状态的变化inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());inCallService.onCanAddCallChanged(mCallsManager.canAddCall());} catch (RemoteException ignored) {Log.e(this, ignored, "changed event failed");}mBindingFuture.complete(true);Log.i(this, "%s calls sent to InCallService.", numCallsSent);Trace.endSection();return true;}

lnCallController 通过绑定服务的方式,开启拨号流程中的第二次跨进程访问,从Telecom 应用的system_serve进程再次回到Dialer应用的com.android .dialer 进程。

com.android.dialer 进程

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

Used to receive updates about calls from the Telecom component. This service is bound to Telecom while there exist calls which potentially require UI. This includes ringing (incoming), dialing (outgoing), and active calls. When the last call is disconnected, Telecom will unbind to the service triggering InCallActivity (via CallList) to finish soon after.

用于接收来自电信组件的有关呼叫的更新。当存在可能需要UI的调用时,此服务绑定到电信。这包括振铃(传入)、拨号(传出)和活动呼叫。当最后一个呼叫断开时,电信将解除与服务的绑定,并触发InCallActivity(通过CallList)在不久之后完成。

InCallServiceImpl 的核心功能 是在它的父类 InCallService, InCallServiceImpl 被绑定时,返回一个 InCallServiceBinder, 因此 system 进程拿到的接口就是这个, 绑定之前,会先初始化 InCallPresenter, InCallPresenter 是全局唯一(单例)的, 它负责 更新 电话APP的 UI。(MVP 模式)

  • apps/Dialer/java/com/android/incallui/InCallServiceImpl.java

  @Overridepublic IBinder onBind(Intent intent) {Trace.beginSection("InCallServiceImpl.onBind");final Context context = getApplicationContext();final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);AudioModeProvider.getInstance().initializeAudioState(this);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),speakEasyCallManager);InCallPresenter.getInstance().onServiceBind(); // 设置服务绑定状态mServiceBound 属性InCallPresenter.getInstance().maybeStartRevealAnimation(intent); // 开启ActivityTelecomAdapter.getInstance().setInCallService(this);// 保存服务returnToCallController =new ReturnToCallController(this, ContactInfoCache.getInstance(context));feedbackListener = FeedbackComponent.get(context).getCallFeedbackListener();CallList.getInstance().addListener(feedbackListener);IBinder iBinder = super.onBind(intent); // 调用父类InCallService 的onBind 方法Trace.endSection();return iBinder;}
  • telecom/InCallService.java

    @Override
421      public IBinder onBind(Intent intent) {
422          return new InCallServiceBinder();
423      }

转到 InCallServiceBinder

InCallServiceBinder 实现了IInCallService . aidl 的接口,这些接口通过发送Handler 消息, 将服务
接收到的服务请求转化为异步处理方式

    /** Manages the binder calls so that the implementor does not need to deal with it. */
// InCallServiceBinder 实现了IInCallService . aidl 的接口,这些接口通过发送Handler 消息, 将服务
//接收到的服务请求转化为异步处理方式
private final class InCallServiceBinder extends IInCallService.Stub {@Overridepublic void setInCallAdapter(IInCallAdapter inCallAdapter) {mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();}@Overridepublic void addCall(ParcelableCall call) {mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();}@Overridepublic void updateCall(ParcelableCall call) {mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();}

mHandler 发送Handler 消息将llnCallService 的同步调用转换为异步处理,在后续的setlnCallAdapter  和  addCall 接口响应中继续分析其实现逻辑。

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

InCallPresenter 的 maybeStartRevealAnimation 会启动 InCallActivity, 即 拨号进行中的界面(或者是来电界面)

  public void maybeStartRevealAnimation(Intent intent) {if (intent == null || inCallActivity != 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);setBoundAndWaitingForOutgoingCall(true, accountHandle);if (shouldStartInBubbleModeWithExtras(extras)) {LogUtil.i("InCallPresenter.maybeStartRevealAnimation", "shouldStartInBubbleMode");// Show bubble instead of in call UIreturn;}final Intent activityIntent =InCallActivity.getIntent(context, false, true, false /* forFullScreen */);activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);context.startActivity(activityIntent);}

其中:getInCallIntent 就是创建一个启动 InCallActivity 的 Intent.

  • android/incallui/InCallActivity.java

  public static Intent getIntent(Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {Intent intent = new Intent(Intent.ACTION_MAIN, null);intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);intent.setClass(context, InCallActivity.class);if (showDialpad) {intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);}intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);return intent;}

至此, 拨号进行中的界面起来了, 接下来查看 InCallServiceBinder 里面的实现,setInCallAdapter 和 addCall。

链接:

  • frameworks/base/telecomm/java/android/telecom/InCallService.java

InCallServiceBinder 收到消息,是直接转交给 mHandler  

    /** Manages the binder calls so that the implementor does not need to deal with it. */private final class InCallServiceBinder extends IInCallService.Stub {@Overridepublic void setInCallAdapter(IInCallAdapter inCallAdapter) {mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();}@Overridepublic void addCall(ParcelableCall call) {mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();}

看一下 mHandler 对象,在 case MSG_SET_IN_CALL_ADAPTER 中创建了 Phone 对象

Phone 在UI层面上, Phone是唯一的,不分类型,而且数量只有一个

private final Handler mHandler = new Handler(Looper.getMainLooper()) {@Override
public void handleMessage(Message msg) {// ...switch (msg.what) {case MSG_SET_IN_CALL_ADAPTER:// setInCallAdapter 触发创建 com.android.dialer 的唯一 的phone对象// 也就是说, 在 com.android.dialer 进程的所有操作 都是经过 phone 里面的mInCallAdapter 发送到 system 进程的 (框架层)mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));// 核心, 让InCallService 监听 Phone 的状态变化mPhone.addListener(mPhoneListener);onPhoneCreated(mPhone);break;case MSG_ADD_CALL:mPhone.internalAddCall((ParcelableCall) msg.obj);break;case MSG_UPDATE_CALL:mPhone.internalUpdateCall((ParcelableCall) msg.obj);break;// ...// 省略大量消息类型}}
  • telecomm/java/android/telecom/Phone.java

mPhone.internalAddCall 创建一个本地的Telecom Call (不同于system进程的Call, 下面简称TCall),通知 InCallService

final void internalAddCall(ParcelableCall parcelableCall) {Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,parcelableCall.getState());mCallByTelecomCallId.put(parcelableCall.getId(), call);mCalls.add(call);checkCallTree(parcelableCall);call.internalUpdate(parcelableCall, mCallByTelecomCallId);// 核心, 通知 InCallService 有添加了一个Call//private void fireCallAdded(Call call) {//    for (Listener listener : mListeners) {//        listener.onCallAdded(this, call);//    }//}fireCallAdded(call);}

其中的 fireCallAdded方法中的 mListeners 如下表示

这其实是个空实现,具体实现是在子类中,继续跟进到子类 InCallServiceImpl  中分析

    private Phone.Listener mPhoneListener = new Phone.Listener() {/** ${inheritDoc} */@Overridepublic void onAudioStateChanged(Phone phone, AudioState audioState) {InCallService.this.onAudioStateChanged(audioState);}
。。。。。///** ${inheritDoc} */@Overridepublic void onCallAdded(Phone phone, Call call) {InCallService.this.onCallAdded(call);}

  • incallui/InCallServiceImpl.java

mPhoneListener.onCallAdded => InCallServiceImpl.onCallAdded

public void onCallAdded(Call call) {// 核心, 主过程//CallList.getInstance().onCallAdded(call);// 绑定监听 TCall 的状态变化InCallPresenter.getInstance().onCallAdded(call);}
  • packages/apps/Dialer/java/com/android/incallui/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();//CallList(Call的维护列表)调用onCallAddedmCallList.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);}
  • packages/apps/Dialer/java/com/android/incallui/call/CallList.java

129    public void onCallAdded(
130        final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
131      Trace.beginSection("CallList.onCallAdded");216      if (call.getState() == DialerCallState.INCOMING
217          || call.getState() == DialerCallState.CALL_WAITING) {
218        if (call.isActiveRttCall()) {
219          if (!call.isPhoneAccountRttCapable()) {
220            RttPromotion.setEnabled(context);
221          }
222          Logger.get(context)
223              .logCallImpression(
224                  DialerImpression.Type.INCOMING_RTT_CALL,
225                  call.getUniqueCallId(),
226                  call.getTimeAddedMs());
227        }
// 来电进行处理
228        onIncoming(call);
229      } else {
// 对拨号进行处理230        if (call.isActiveRttCall()) {
231          Logger.get(context)
232              .logCallImpression(
233                  DialerImpression.Type.OUTGOING_RTT_CALL,
234                  call.getUniqueCallId(),
235                  call.getTimeAddedMs());
236        }
237        onUpdateCall(call);
// 核心代码
238        notifyGenericListeners();

CallList 维护电话列表

CallList维护着一个打电话列表 (CallList里面的Call简称为 LCall, 区别于上面的 TCall), 每一个 LCall 维护着一个 TCall.

  • TCall 是由 Phone 维护的
  • LCall 是由 CallList 维护的
  • TCall 和 LCall 一一对应

继续跟踪 notifyGenericListeners 方法

633    private void notifyGenericListeners() {
634      Trace.beginSection("CallList.notifyGenericListeners");
635      for (Listener listener : listeners) {
636        listener.onCallListChange(this);
637      }
638      Trace.endSection();
639    }

跟踪 监听者 listeners

  • packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java

下面查找谁监听了 CallListInCallPresenter 实现了 CallList.Listener 接口, 所以 InCallPresenter.onCallListChange 会被触发

public class InCallPresenter implements CallList.Listener, AudioModeProvider.AudioModeListener {

跟踪到 onCallListChange 方法

public void onCallListChange(CallList callList) {// ...// 根据新状态,决定是否打开 InCallActivity, 或则关闭 InCallActivity, 确保不会重复打开newState = startOrFinishUi(newState);for (InCallStateListener listener : mListeners) {// 凡是监听 InCallPresenter 的监听者,都得到通知。listener.onStateChange(oldState, mInCallState, callList);}}

上述涉及到 InCallStateListener  监听者,是一个接口,它定义了一个方法onStateChange

再谈 InCallActivity

InCallActivity 展示拨号,或者来电状态

InCallActivity 是通过 展示 或隐藏 Fragment 来 区分不同状态的, 拨号进行中的Fragment 是 DialpadFragment , 而 DialpadFragment 对应的 Presenter 是 DialpadPresenter, onUiReady

InCallPresenter.java直接控制着InCallActivity,与此同时,通过一些监听器,例如IncomingCallListener/ CanAddCallListener/ InCallDetailsListener等 控制着AnswerPresenter等等。

AnswerPresenter控制着AnswerFragment的显示,是InCallActivity界面的一部分;

VideoCallPresenter控制着VideoCallFragment的显示,是InCallActivity界面的一部分;

CallCardPresenter控制着CallCardFragment的显示,是InCallActivity界面的一部分;

CallButtonPresenter控制着CallButtonFragment的显示,是InCallActivity界面的一部分;

这些Presenter一般会实现InCallPresenter的监听器对应的方法。

其中:

CallCardFragment:用于显示联系人信息及通话时间等;
CallButtonFragment:通话界面下方的控制按钮。
DialpadFragment:拨号盘显示控件。
AnswerFragment:来电控制控件,用于操作接听/拒接/短信快捷回复。
ConferenceManagerFragment:会议电话的界面。
VideoCallFragment:视屏通话控件,在CallCardFragment中调用。

如下

  public void onUiReady(DialpadUi ui) {super.onUiReady(ui);InCallPresenter.getInstance().addListener(this);call = CallList.getInstance().getOutgoingOrActive();}

所以 DialpadFragment .onStateChange 会被触发 接着就是更新UI了(MVP)

  • packages/apps/Dialer/java/com/android/incallui/DialpadFragment.java

  private static final Map<Integer, Character> displayMap = new ArrayMap<>();/** Set up the static maps */static {// Map the buttons to the display charactersdisplayMap.put(R.id.one, '1');displayMap.put(R.id.two, '2');displayMap.put(R.id.three, '3');displayMap.put(R.id.four, '4');displayMap.put(R.id.five, '5');displayMap.put(R.id.six, '6');displayMap.put(R.id.seven, '7');displayMap.put(R.id.eight, '8');displayMap.put(R.id.nine, '9');displayMap.put(R.id.zero, '0');displayMap.put(R.id.pound, '#');displayMap.put(R.id.star, '*');}

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

第二个分支:CallsManager.placeOutgoingCall

执行 broadcaster. processCall () 方法

  • packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

    public void processCall(CallDisposition disposition) {if (disposition.callImmediately) {boolean speakerphoneOn = mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);int videoState = mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,VideoProfile.STATE_AUDIO_ONLY);placeOutgoingCallImmediately(mCall, disposition.callingAddress, null,speakerphoneOn, videoState);

其中的  placeOutgoingCallImmediately 方法

    private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,boolean speakerphoneOn, int videoState) {Log.i(this,"Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");// Since we are not going to go through "Outgoing call broadcast", make sure// we mark it as ready.mCall.setNewOutgoingCallIntentBroadcastIsDone();mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);}
  • packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

CallsManager.placeOutgoingCall

2048      @VisibleForTesting
2049      public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
2050              boolean speakerphoneOn, int videoState) {
2051          if (call == null) {
2052              // don't do anything if the call no longer exists
2053              Log.i(this, "Canceling unknown call.");
2054              return;
2055          }
2056
2057          final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
2058
2059          if (gatewayInfo == null) {
2060              Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
2061          } else {
2062              Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
2063                      Log.pii(uriHandle), Log.pii(handle));
2064          }
2065
2066          call.setHandle(uriHandle);
2067          call.setGatewayInfo(gatewayInfo);
2068
2069          final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
2070                  R.bool.use_speaker_when_docked);
2071          final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
2072          final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
2073
2074          // Auto-enable speakerphone if the originating intent specified to do so, if the call
2075          // is a video call, of if using speaker when docked
2076          call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
2077                  || (useSpeakerWhenDocked && useSpeakerForDock));
2078          call.setVideoState(videoState);
2079
2080          if (speakerphoneOn) {
2081              Log.i(this, "%s Starting with speakerphone as requested", call);
2082          } else if (useSpeakerWhenDocked && useSpeakerForDock) {
2083              Log.i(this, "%s Starting with speakerphone because car is docked.", call);
2084          } else if (useSpeakerForVideoCall) {
2085              mInVideoMode = true; // UNISOC: add for bug1152803
2086              Log.i(this, "%s Starting with speakerphone because its a video call.", call);
2087          }
2088
2089          if (call.isEmergencyCall()) {
2090              new AsyncEmergencyContactNotifier(mContext).execute();
2091          }
2092
2093          final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
2094                  com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
2095          final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
2096                  call.getTargetPhoneAccount());
2097          final String callHandleScheme =
2098                  call.getHandle() == null ? null : call.getHandle().getScheme();
2099          if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
2100              // If the account has been set, proceed to place the outgoing call.
2101              // Otherwise the connection will be initiated when the account is set by the user.
2102              if (call.isSelfManaged() && !isOutgoingCallPermitted) {
2103                  notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
2104              } else {
2105                  if (call.isEmergencyCall()) {
2106                      // Drop any ongoing self-managed calls to make way for an emergency call.
2107                      disconnectSelfManagedCalls("place emerg call" /* reason */);
2108                  }
2109
2110                  call.startCreateConnection(mPhoneAccountRegistrar);
2111              }
2112          } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
2113                  requireCallCapableAccountByHandle ? callHandleScheme : null, false,
2114                  call.getInitiatingUser()).isEmpty()) {
2115              // If there are no call capable accounts, disconnect the call.
2116              markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
2117                      "No registered PhoneAccounts"));
2118              markCallAsRemoved(call);
2119          }
2120      }

Call 由 CallsManager 维护的拨号,或者来电实例

  • packages/services/Telecomm/src/com/android/server/telecom/Call.java

Call.startCreateConnection

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {if (mCreateConnectionProcessor != null) {Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +" due to a race between NewOutgoingCallIntentBroadcaster and " +"phoneAccountSelected, but is harmlessly resolved by ignoring the second " +"invocation.");return;}mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,phoneAccountRegistrar, mContext);mCreateConnectionProcessor.process();}
  • packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

CreateConnectionProcessor.process ==> CreateConnectionProcessor.attemptNextPhoneAccount

    @VisibleForTestingpublic void process() {Log.v(this, "process");clearTimeout();mAttemptRecords = new ArrayList<>();if (mCall.getTargetPhoneAccount() != null) {mAttemptRecords.add(new CallAttemptRecord(mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));}if (!mCall.isSelfManaged()) {adjustAttemptsForConnectionManager();adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());}mAttemptRecordIterator = mAttemptRecords.iterator();attemptNextPhoneAccount();}

attemptNextPhoneAccount 方法

private ConnectionServiceWrapper mService;private void attemptNextPhoneAccount() {//。。。//if (mCallResponse != null && attempt != null) {Log.i(this, "Trying attempt %s", attempt);PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;mService = mRepository.getService(phoneAccount.getComponentName(),phoneAccount.getUserHandle());if (mService == null) {Log.i(this, "Found no connection service for attempt %s", attempt);attemptNextPhoneAccount();} else {mConnectionAttempt++;mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);mCall.setConnectionService(mService);setTimeoutIfNeeded(mService, attempt);if (mCall.isIncoming()) {mService.createConnection(mCall, CreateConnectionProcessor.this);} else {// Start to create the connection for outgoing call after the ConnectionService// of the call has gained the focus.mCall.getConnectionServiceFocusManager().requestFocus(mCall,new CallsManager.RequestCallback(new CallsManager.PendingAction() {@Overridepublic void performAction() {Log.d(this, "perform create connection");mService.createConnection(mCall,CreateConnectionProcessor.this);}}));}}} else {Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);notifyCallConnectionFailure(disconnectCause);}

ConnectionServiceWrapper 负责和 Telephony 沟通

  • packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

mService.createConnection(mCall, CreateConnectionProcessor.this); 开始进行链接

void createConnection(final Call call, final CreateConnectionResponse response) {// 链接创建成功后 onSuccess 会被调用BindCallback callback = new BindCallback() {@Overridepublic void onSuccess() {try {/// M: For VoLTE @{boolean isConferenceDial = call.isConferenceDial();// 会议通话if (isConferenceDial) {logOutgoing("createConference(%s) via %s.", call, getComponentName());mServiceInterface.createConference(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState()),call.getConferenceParticipants(),call.isIncoming());// 非会议(拨打电话会走这里)} else {// 通过远程接口创建链接mServiceInterface.createConnection(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState()),call.isIncoming(),call.isUnknown());}/// @}} catch (RemoteException e) {}}@Overridepublic void onFailure() {}};mBinder.bind(callback, call);
}

mBinder是Binder2对象,Binder2是ConnectionServiceWrapper的父类ServiceBinder内部类,所以此处调用的是的ServiceBinder的内部类的Binder2类的bind()方法,先new一个ServiceConnection对象,然后绑定一个远程服务端服务。

如果绑定成功的话,在ServiceBinder的内部类ServiceBinderConnection的onServiceConnected()方法就被调用。
在这里做了两件事:

1).通过setBinder()方法,回调ConnectionServiceWrapper的setServiceInterface()方法,通过mServiceInterface = IConnectionService.Stub.asInterface(binder);
这行代码获取一个远程服务端的对象mServiceInterface 。
  2).再通过调用handleSuccessfulConnection()方法回调callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法里。调用ConnectionService.java里mBinder的createConnection()方法然后通过message传递调用createConnection()方法。

  • packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java

mBider 的bind 方法

void bind(BindCallback callback, Call call) {if (mServiceConnection == null) {Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);ServiceConnection connection = new ServiceBinderConnection(call);// 进行绑定if (mUserHandle != null) {isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,mUserHandle);} else {isBound = mContext.bindService(serviceIntent, connection, bindingFlags);}if (!isBound) {handleFailedConnection();return;}} else {}}

绑定成功后,ServiceBinderConnection 的 onServiceConnected 会触发

        @Overridepublic void onServiceConnected(ComponentName componentName, IBinder binder) {try {if (binder != null) {mServiceDeathRecipient = new ServiceDeathRecipient(componentName);try {binder.linkToDeath(mServiceDeathRecipient, 0);mServiceConnection = this;
// 会触发 ConnectionServiceWrapper.setServiceInterface  ==>
ConnectionServiceWrapper.addConnectionServiceAdapter
通过mServiceInterface,给绑定的服务,提供一个访问自己的接口setBinder(binder);// 触发bind(BindCallback callback, Call call) 中 callback 的 onSuccesshandleSuccessfulConnection();} catch (RemoteException e) {Log.w(this, "onServiceConnected: %s died.");if (mServiceDeathRecipient != null) {mServiceDeathRecipient.binderDied();}}}}} finally {Log.endSession();}}

整个绑定过程,只做2件事,

一是给远程服务提供访问自己的接口,

二是利用远程接口创建一个通话链接。

这2件事都是跨进程进行的。远程服务访问自己的接口是 ConnectionServiceWrapper.Adapter , 是一个Binder。

  • packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

ConnectionServiceWrapper.Adapter 提供了一套更新Call 状态的 操作, 因为目前分析的是拨打电话流程,所以先分析setDialing

由内部类 Adapter 继承于 IConnectionServiceAdapter.Stub,可以看出会进行跨进程访问

public class ConnectionServiceWrapper extends ServiceBinder implementsConnectionServiceFocusManager.ConnectionServiceFocus {private final class Adapter extends IConnectionServiceAdapter.Stub {

  • packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java

调用ConnectionService.java里mBinder的createConnection()方法然后通过message传递调用createConnection()方法。

    private void handleSuccessfulConnection() {// Make a copy so that we don't have a deadlock inside one of the callbacks.Set<BindCallback> callbacksCopy = new ArraySet<>();synchronized (mCallbacks) {callbacksCopy.addAll(mCallbacks);mCallbacks.clear();}for (BindCallback callback : callbacksCopy) {callback.onSuccess();}}

通过调用handleSuccessfulConnection()方法回调callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法里。

1094      public void createConnection(final Call call, final CreateConnectionResponse response) {
1095          Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
1096          BindCallback callback = new BindCallback() {
1097              @Override
1098              public void onSuccess() {
1099                  String callId = mCallIdMapper.getCallId(call);
1100                  mPendingResponses.put(callId, response);
1101
///---------//1146
1150          // 核心代码
1151                  try {
1152                      mServiceInterface.createConnection(
1153                              call.getConnectionManagerPhoneAccount(),
1154                              callId,
1155                              connectionRequest,
1156                              call.shouldAttachToExistingConnection(),
1157                              call.isUnknown(),
1158                              Log.getExternalSession());
1159
1160                  } catch (RemoteException e) {
1161                      Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
1162                      mPendingResponses.remove(callId).handleCreateConnectionFailure(
1163                              new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1164                  }
1165              }
1166
1167              @Override
1168              public void onFailure() {
1169                  Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1170                  response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1171              }
1172          };
1173
1174          mBinder.bind(callback, call);
1175      }
private IConnectionService mServiceInterface;

  • frameworks/base/telecomm/java/android/telecom/ConnectionService.java

createConnection()方法通过判断是来电还是去电分别创建不同的connection,

去电则调用onCreateOutgoingConnection(),

TelephonyConnectionService是ConnectionService的实例,所以进入TelephonyConnectionService.java的onCreateOutgoingConnection()方法。

    private void createConnection(final PhoneAccountHandle callManagerAccount,final String callId,final ConnectionRequest request,boolean isIncoming,boolean isUnknown) {boolean isLegacyHandover = request.getExtras() != null &&request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +"isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,isHandover);Connection connection = null;if (isHandover) {PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null? (PhoneAccountHandle) request.getExtras().getParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;if (!isIncoming) {connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);} else {connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);}} else {connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ? onCreateIncomingConnection(callManagerAccount, request): onCreateOutgoingConnection(callManagerAccount, request);}

  • packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java

TelephonyConnectionService是ConnectionService的实例,所以进入TelephonyConnectionService.java的onCreateOutgoingConnection()方法。

@Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,final ConnectionRequest request) {//这里做了很多判断,返回失败的连接(比如拨打号码为空,未指定sim卡,设置为仅4G等)......// Get the right phone object from the account data passed in.final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,/* Note: when not an emergency, handle can be null for unknown callers */handle == null ? null : handle.getSchemeSpecificPart());if (!isEmergencyNumber) {final Connection resultConnection = getTelephonyConnection(request, numberToDial,false, handle, phone);return placeOutgoingConnection(request, resultConnection, phone);} else {final Connection resultConnection = getTelephonyConnection(request, numberToDial,true, handle, phone);CompletableFuture<Boolean> phoneFuture = delayDialForDdsSwitch(phone);phoneFuture.whenComplete((result, error) -> {if (error != null) {Log.w(this, "onCreateOutgoingConn - delayDialForDdsSwitch exception= "+ error.getMessage());}Log.i(this, "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);placeOutgoingConnection(request, resultConnection, phone);});return resultConnection;}......//如果上面的都不是则执行这里placeOutgoingConnection(connection, phone, request);
}

继续跟踪placeOutgoingConnection()方法处理拨号流程

    private void placeOutgoingConnection(TelephonyConnection connection, Phone phone, ConnectionRequest request) {placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());}

又进入到另外一个 placeOutgoingConnection() 方法中

private void placeOutgoingConnection(TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {String number = connection.getAddress().getSchemeSpecificPart();boolean isAddParticipant = (extras != null) && extras.getBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, false);Log.d(this, "placeOutgoingConnection isAddParticipant = " + isAddParticipant);updatePhoneAccount(connection, phone);com.android.internal.telephony.Connection originalConnection = null;try {if (phone != null) {if (isAddParticipant) {phone.addParticipant(number);// 为紧急号码做一些处理return;} else {
// 核心代码originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()    //呼叫.setVideoState(videoState).setIntentExtras(extras).setRttTextStream(connection.getRttTextStream()).build());}}} catch (CallStateException e) {     // 失败处理Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;if (e.getError() == CallStateException.ERROR_OUT_OF_SERVICE) {cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;} else if (e.getError() == CallStateException.ERROR_POWER_OFF) {cause = android.telephony.DisconnectCause.POWER_OFF;}connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(), phone.getPhoneId()));connection.clearOriginalConnection();connection.destroy();return;}if (originalConnection == null) { // 失败处理int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;// On GSM phones, null connection means that we dialed an MMI codeif (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {Log.d(this, "dialed MMI code");int subId = phone.getSubId();Log.d(this, "subId: "+subId);telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;final Intent intent = new Intent(this, MMIDialogActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);if (SubscriptionManager.isValidSubscriptionId(subId)) {intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);}startActivity(intent);}Log.d(this, "placeOutgoingConnection, phone.dial returned null");connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, "Connection is null", phone.getPhoneId()));connection.clearOriginalConnection();connection.destroy();} else {connection.setOriginalConnection(originalConnection); // TelephonyConnection 监听 phone, 更新时,从originalConnection里面取。//TelephonyConnection.setOriginalConnection 的实现, 目的主要是为将 mHandler 注册注册到
//phone 的监听者列表里面, phone 变化, 会触发 mHandler 里面的handleMessage 被调用。}
  • frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

调用Phone的dial()方法后就进入了Telephony Framework层了。Android 7.0中把GSMPhone和CDMAPhone全部集成到GsmCdmaPhone中,继续看GsmCdmaPhone中dial()

 @Overridepublic Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)throws CallStateException {.......//ImsPhone是专门处理VoLTE的一个类if (videoState == VideoProfile.STATE_AUDIO_ONLY) {if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call");//return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);} else {if (SystemProperties.get("persist.mtk_vilte_support").equals("1")) {//支持VoLTe  Ims ps video callreturn imsPhone.dial(dialString, uusInfo, videoState, intentExtras);} else {//cs video callreturn dialInternal(dialString, uusInfo, videoState, intentExtras);/// @}}.......if (isPhoneTypeGsm()) {/// M: CC: For 3G VT only @{//return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);return dialInternal(dialString, null, videoState, intentExtras);/// @}} else {return dialInternal(dialString, null, videoState, intentExtras);}}

继续看dialInternal() 方法,这里有 2 个

    @Overrideprotected Connection dialInternal(String dialString, DialArgs dialArgs)throws CallStateException {return dialInternal(dialString, dialArgs, null);}protected Connection dialInternal(String dialString, DialArgs dialArgs,ResultReceiver wrappedCallback)throws CallStateException {// Need to make sure dialString gets parsed properlyString newDialString = PhoneNumberUtils.stripSeparators(dialString);if (isPhoneTypeGsm()) {// handle in-call MMI first if applicableif (handleInCallMmiCommands(newDialString)) {return null;}// Only look at the Network portion for mmiString networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this,mUiccApplication.get(), wrappedCallback);if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");if (mmi == null) {return mCT.dialGsm(newDialString, dialArgs.uusInfo, dialArgs.intentExtras);} else if (mmi.isTemporaryModeCLIR()) {return mCT.dialGsm(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,dialArgs.intentExtras);} else {mPendingMMIs.add(mmi);mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));mmi.processCode();return null;}} else {
// 核心方法return mCT.dial(newDialString, dialArgs.intentExtras);}}

mCT是GsmCdmaCallTracker对象,我们接着看GsmCdmaCallTracker中的dial()

  • frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java

依据不同制式 拨打电话

    public Connection dial(String dialString, Bundle intentExtras) throws CallStateException {if (isPhoneTypeGsm()) {return dialGsm(dialString, CommandsInterface.CLIR_DEFAULT, intentExtras);} else {return dialCdma(dialString, CommandsInterface.CLIR_DEFAULT, intentExtras);}}

继续跟踪 diaCdma 代码

315      public synchronized Connection dialGsm(String dialString, int clirMode, UUSInfo uusInfo,
316                                          Bundle intentExtras)
317              throws CallStateException {369
370
371          if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
372                  || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
373              // Phone number is invalid
374              mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
375
376              // handlePollCalls() will notice this call not present
377              // and will mark it as dropped.
378              pollCallsWhenSafe();
379          } else {
380              // Always unmute when initiating a new call
381              setMute(false);382          //mCi的类型为CommandsInterface,在创建phone的时候作为参数,实际上执行的是RIL.dial()
383              mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(),
384                      mPendingMO.getEmergencyNumberInfo(), mPendingMO.hasKnownUserIntentEmergency(),
385                      clirMode, uusInfo, obtainCompleteMessage());
386          }
387
388          if (mNumberConverted) {
389              mPendingMO.setConverted(origNumber);
390              mNumberConverted = false;
391          }
392
393          updatePhoneState();
394          mPhone.notifyPreciseCallStateChanged();
395
396          return mPendingMO;
397      }

其中mCi就是RIL的实例,mCi是CommandsInterface类型,在GsmCdmaPhone的构建中获取的

public static void makeDefaultPhone(Context context) {synchronized (sLockProxyPhones) {......sCommandsInterfaces[i] = new RIL(context, networkModes[i],cdmaSubscription, i);......phone = new GsmCdmaPhone(context,sCommandsInterfaces[i], sPhoneNotifier, i,PhoneConstants.PHONE_TYPE_CDMA_LTE,TelephonyComponentFactory.getInstance());......}
  • frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java

    @Overridepublic void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo,Message result) {riljLog("dial" + "> " + "isEmergencyCall " + isEmergencyCall + "  emergencyNumberInfo: " + emergencyNumberInfo+ " mRadioVersion " + mRadioVersion);if (isEmergencyCall && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)&& emergencyNumberInfo != null) {emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode,uusInfo, result);return;}IRadio radioProxy = getRadioProxy(result);if (radioProxy != null) {RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,mRILDefaultWorkSource);Dial dialInfo = new Dial();dialInfo.address = convertNullToEmptyString(address);dialInfo.clir = clirMode;if (uusInfo != null) {UusInfo info = new UusInfo();info.uusType = uusInfo.getType();info.uusDcs = uusInfo.getDcs();info.uusData = new String(uusInfo.getUserData());dialInfo.uusInfo.add(info);}if (RILJ_LOGD) {// Do not log function arg for privacyriljLog(rr.serialString() + "> " + requestToString(rr.mRequest));}try {radioProxy.dial(rr.mSerial, dialInfo);} catch (RemoteException | RuntimeException e) {handleRadioProxyExceptionForRR(rr, "dial", e);}}}

RIL与通话模块底层通讯过程

RIL通讯主要由RILSender和RILReceiver构成,用于通讯传输的载体的有 RILRequest,
Registrant(RegistrantList)。

  1. RILSender
    是一个Handler, 通过 #send(RILRequest) 将请求发送到 mSenderThread线程中,handleMessage 将请求写入mSocket中。
  2. RILReceiver
    在run内无限轮询,一旦读取到通话底层返回的数据,交给 #processResponse(Parcel)处理。
    其中应答分为有请求的应答,无请求的应答(即状态改变后的反馈)
    1.RIL_REQUEST_xxxx 有请求
    2.RIL_UNSOL_xxxx 无请求
  3. RILRequest
    成员变量:
    1.mSerial 请求序号,唯一,保证请求和反馈的一致。
    2.mRequest 请求类型 即 RIL_xxxx。
    3.mResult 用来发送请求的结果的句柄,由RIL请求的调用者提供。
  4. Registrant
           在 RIL内部维护着一系列的RegistrantList(Registrant的集合),每个集合代表一种状态改变的类型,对于有请求的应答,RIL是通过对应的mResult来发送结果的,但是对于无请求的应答,RIL是将反馈通知告诉对应的RegistrantList的(#notifyRegistrants),RegistrantList会通知每一个Registrant。在RIL的直接父类定义了这些RegistrantList,并提供了注册到RegistrantList的方法(eg.#registerForCallStateChanged,#unregisterForCallStateChanged)所以,如果想要监听通话状态的变化,那么就需要注册监听到对应的RegistrantList(监听着需要提供一个handler和一个int和object,handler用来发送数据,int区分监听的事件,object是额外的信息)。

安卓10拨号流程梳理相关推荐

  1. 安卓10拨号流程详细总结

    电话应用框架 Android电话模块是一个典型的分层结构设计,如下: 电话框架分为4个层次,分别为:应用层.框架层(framework层,简称fw).RIL(Radio Interface Layer ...

  2. 安卓10 来电流程梳理

    来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程中 Telephony 直接与Moderm层交互, com.android.phone 进程 ...

  3. 安卓10来电流程详细总结

    来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程中 Telephony 直接与Moderm层交互, com.android.phone 进程 ...

  4. android 刷系统,安卓10的刷机教程,教你刷好Killer的精简包

    本帖最后由 大熊花 于 2020-6-13 22:03 编辑 安卓10真香-- 之前我还退回到安卓9的公测33,心想为了玩游戏流畅,结果发现还是安卓10更好一点. 下午把东西准备齐了刷了killer的 ...

  5. ec200t 拨号_移远EC20 R2.0 AT指令拨号流程

    使用AT指令进行拨号,可快速的集成EC20 4G模块,只需要给EC20供电和接上通信UART串口,就能实现网络连接. EC20管脚分配 最小系统电路图 TCP/IP AT命令拨号流程 AT指令拨号流程 ...

  6. 开发安卓app游戏_「安卓APP开发流程」安卓APP如何开发的?

    21世纪,智能手机走进了人们的生活,现在的智能手机的操作系统基本分为两种,一种是IOS系统(苹果系统).安卓系统,其中,安卓系统是开源的,所以很多品牌商会讲安卓包装成自己的系统,但核心还是一样的,都是 ...

  7. android10rom包,安卓10的刷机教程,教你刷好Killer的精简包

    本帖最后由 大熊花 于 2020-6-13 22:03 编辑 安卓10真香-- 之前我还退回到安卓9的公测33,心想为了玩游戏流畅,结果发现还是安卓10更好一点. 下午把东西准备齐了刷了killer的 ...

  8. linux抓包pppoe,pppoe抓包流程和拨号流程

    pppoe拨号流程 PPPoE(Point to Point Protocol over Ethernet,基于以太网的点对点协议)的工作流程包含发现(Discovery)和会话(Session)两个 ...

  9. Telephony 拨号流程

    一.进入Dialer App 使用adb logcat -vtime -b events命令查看手机的运行日志,最后操作手机,点击dialer进入拨号界面. 05-26 19:20:41.633 I/ ...

最新文章

  1. sqlinesdata教程_如何将Oracle数据导入MySQL
  2. Nature官方劝退读博:全球七成博士对前途迷茫,36%自认有过心理疾病
  3. vue2 构建一个旅游类WebApp
  4. 探臻实录 | 戴琼海:搭建脑科学与人工智能的桥梁
  5. php压制错误的代码,为什么要压制PHP错误?
  6. ArrayList 与 LinkedList 底层实现
  7. 微信公众平台开发—利用OAuth2.0获取微信用户基本信息
  8. SpringBoot中Bean按条件装配
  9. sum函数两个同行的元素相加_Excel函数公式大全:利用sum函数进行汇总以及对多个汇总进行求和...
  10. CheckPoint
  11. SQL Server 2008处理隐式数据类型转换在执行计划中的增强
  12. 【MySQL】sql语句中exists和in有何区别?
  13. 高交会|华创芯光邀您一起畅游可见光通信的世界
  14. 谭浩强第五版课后习题答案-----第三章
  15. 承上启下继往开来,Python3上下文管理器(ContextManagers)与With关键字的迷思
  16. 什么是中台,为什么要中台?一篇文章带你了解中台的概念!
  17. HTML识别文本空格回车换行展示
  18. 程序员是学历与能力,哪个重要呢?本文告诉你!
  19. MQ(message queue)使用 Spring整合 MQ下载 五分钟上手使用
  20. 1.DLL注入相关概念

热门文章

  1. 豆豆趣事[2015年10月]
  2. 微信自动回复指定联系人
  3. 【java】一个int占多少个字节?
  4. Altair收购EMSS公司 增强电磁求解器功能
  5. 在线支付接口开发总结
  6. 个人免签支付接口详解
  7. 《2020-2021年中国云管理平台市场现状与发展趋势研究报告》发布:华云数据连续三年稳居领导者象限
  8. 如何实现5G和WiFi 6E网络?如何部署可编程测试系统(一)
  9. 自动驾驶采标系列七:复杂场景语义理解-可行驶区域检测
  10. win 10系统引导被删,开机报错0xc0000098