赞
踩
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,负责实际的无线通信能力处理
用户点击拨号盘的拨号按钮,此时开始呼叫长征第一步,dialpadfragment的onclick方法会响应点击事件。
- @Override
- public 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(); 方法,判断是否号码是否为空,是否非法等情况
10 之前没有这一步,直接调用 DialerUtils.startActivityWithErrorToast() 方法
携带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();
- }
- }
- 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 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:
创建UserCallIntentProcessorFactory,调用processIntent方法处理呼叫:
- private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
-
- @Override
- public 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);
- }
- }
- }
- }
- }
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);
- }
- }
public static final String ACTION_CALL = "android.intent.action.CALL";
public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
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方法
- 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, ril
- sendNewOutgoingCallIntent(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);
- }
- 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.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通知监听者,添加了一个call
- addCall(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进程的。
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() 方法:
- @VisibleForTesting
- 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)) {
- // 重点关注创建的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 方法,
- @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;
- }
-
- // 指明了绑定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() {
- @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) {
- // 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.ParcelableCall
- parcelableCall = 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 进程。
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 模式)
- @Override
- public 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); // 开启Activity
- TelecomAdapter.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;
- }
- @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 {
- @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();
- }
mHandler 发送Handler 消息将llnCallService 的同步调用转换为异步处理,在后续的setlnCallAdapter 和 addCall 接口响应中继续分析其实现逻辑。
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 UI
- return;
- }
-
- final Intent activityIntent =
- InCallActivity.getIntent(context, false, true, false /* forFullScreen */);
- activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
- context.startActivity(activityIntent);
- }
其中:getInCallIntent 就是创建一个启动 InCallActivity 的 Intent.
- 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。
InCallServiceBinder 收到消息,是直接转交给 mHandler
- /** Manages the binder calls so that the implementor does not need to deal with it. */
- 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();
- }
看一下 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;
- // ...
- // 省略大量消息类型
- }
- }
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} */
- @Override
- public void onAudioStateChanged(Phone phone, AudioState audioState) {
- InCallService.this.onAudioStateChanged(audioState);
- }
- 。。。。。//
-
-
- /** ${inheritDoc} */
- @Override
- public void onCallAdded(Phone phone, Call call) {
- InCallService.this.onCallAdded(call);
- }
mPhoneListener.onCallAdded => InCallServiceImpl.onCallAdded
- public void onCallAdded(Call call) {
- // 核心, 主过程
- //CallList.getInstance().onCallAdded(call);
- // 绑定监听 TCall 的状态变化
- InCallPresenter.getInstance().onCallAdded(call);
- }
- 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的维护列表)调用onCallAdded
- 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);
- }
- 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.
继续跟踪 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
下面查找谁监听了 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)
- private static final Map<Integer, Character> displayMap = new ArrayMap<>();
-
- /** Set up the static maps */
- static {
- // Map the buttons to the display characters
- displayMap.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 () 方法
- 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);
- }
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.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();
- }
CreateConnectionProcessor.process ==> CreateConnectionProcessor.attemptNextPhoneAccount
- @VisibleForTesting
- public 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() {
- @Override
- public 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);
- }
mService.createConnection(mCall, CreateConnectionProcessor.this); 开始进行链接
- void createConnection(final Call call, final CreateConnectionResponse response) {
-
- // 链接创建成功后 onSuccess 会被调用
- BindCallback callback = new BindCallback() {
- @Override
- public 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) {
-
- }
- }
-
- @Override
- public 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()方法。
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 会触发
- @Override
- public 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 的 onSuccess
-
- handleSuccessfulConnection();
- } catch (RemoteException e) {
- Log.w(this, "onServiceConnected: %s died.");
- if (mServiceDeathRecipient != null) {
- mServiceDeathRecipient.binderDied();
- }
- }
- }
- }
- } finally {
- Log.endSession();
- }
- }
整个绑定过程,只做2件事,
一是给远程服务提供访问自己的接口,
二是利用远程接口创建一个通话链接。
这2件事都是跨进程进行的。远程服务访问自己的接口是 ConnectionServiceWrapper.Adapter
, 是一个Binder。
ConnectionServiceWrapper.Adapter
提供了一套更新Call 状态的 操作, 因为目前分析的是拨打电话流程,所以先分析setDialing
由内部类 Adapter 继承于 IConnectionServiceAdapter.Stub,可以看出会进行跨进程访问
- public class ConnectionServiceWrapper extends ServiceBinder implements
- ConnectionServiceFocusManager.ConnectionServiceFocus {
-
- private final class Adapter extends IConnectionServiceAdapter.Stub {
调用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;
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);
- }
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 code
- if (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 被调用。
- }
调用Phone的dial()方法后就进入了Telephony Framework层了。Android 7.0中把GSMPhone和CDMAPhone全部集成到GsmCdmaPhone中,继续看GsmCdmaPhone中dial()
- @Override
- public 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 call
- return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
- } else {
- //cs video call
- return 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 个
- @Override
- protected 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 properly
- String newDialString = PhoneNumberUtils.stripSeparators(dialString);
-
- if (isPhoneTypeGsm()) {
- // handle in-call MMI first if applicable
- if (handleInCallMmiCommands(newDialString)) {
- return null;
- }
-
- // Only look at the Network portion for mmi
- String 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()
依据不同制式 拨打电话
- 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());
- ......
- }
- @Override
- public 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 privacy
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- }
-
- try {
- radioProxy.dial(rr.mSerial, dialInfo);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "dial", e);
- }
- }
- }
RIL通讯主要由RILSender和RILReceiver构成,用于通讯传输的载体的有 RILRequest,
Registrant(RegistrantList)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。