本篇文章介绍M(Marshmallow) Telephony的MO(Mobile Original) call 的流程。
1.拨号App中输入号码并拨号,Android的源生的拨号App为Dialer(packages/apps/Dialer)
下面看一下在输入完号码,点击拨号按钮后,Dialer中都做了什么事情。
首先触发的是UI点击事件
DialpadFragment
handleDialButtonPressed()
/**
* In most cases, when the dial button is pressed, there is a
* number in digits area. Pack it in the intent, start the
* outgoing call broadcast as a separate task and finish this
* activity.
*
* When there is no digit and the phone is CDMA and off hook,
* we're sending a blank flash for CDMA. CDMA networks use Flash
* messages when special processing needs to be done, mainly for
* 3-way or call waiting scenarios. Presumably, here we're in a
* special 3-way scenario where the network needs a blank flash
* before being able to add the new participant. (This is not the
* case with all 3-way calls, just certain CDMA infrastructures.)
*
* Otherwise, there is no digit, display the last dialed
* number. Don't finish since the user may want to edit it. The
* user needs to press the dial button again, to dial it (general
* case described above).
*/
通过注释可以知道这个函数要把要拨打的号码打包到一个intent里,然后发出去。(CDMA的我不懂~~~)
DialerUtils
打包好intent之后,调用DialerUtils的静态方法startActivityWithErrorToast(getActivity(), intent),看一下这个方法要做什么事情。
这个方法估计是要显示一个界面,就是点击拨号按钮之后该显示的界面~
然后,最主要的是有下面这良好代码。
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());
从这两行代码中可以看出,Dialer App拨号需要用到Framework的打电话接口placeCall
并且从TelecomManager的函数原型(placeCall(Uri address, Bundle extras))可以看到,使用这个接口需要提供这两个参数,那么这两个参数分别代表什么意思?
/**
* Places a new outgoing call to the provided address using the system telecom service with
* the specified extras.
*
* This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
* except that the outgoing call will always be sent via the system telecom service. If
* method-caller is either the user selected default dialer app or preloaded system dialer
* app, then emergency calls will also be allowed.
*
* Requires permission: {@link android.Manifest.permission#CALL_PHONE}
*
* Usage example:
* <pre>
* Uri uri = Uri.fromParts("tel", "12345", null);
* Bundle extras = new Bundle();
* extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
* telecomManager.placeCall(uri, extras);
* </pre>
*
* The following keys are supported in the supplied extras.
* <ul>
* <li>{@link #EXTRA_OUTGOING_CALL_EXTRAS}</li>
* <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
* </ul>
*
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
看注释,这方法特别简单粗暴,可以很快的提取需要的了解的信息。这address就是要拨打的电话号码的Uri形式,而extras携带的附加信息可以通过注释了解,各个参数通过字面意思就可以了解个大概~。
2.Telecom的处理,这部分代码很多,需要一步一步的对照着代码来~,不会贴很全的代码,但会罗列主要的代码。
ITelecomService service = getTelecomService();
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
这个service是package/service/Telecomm TelecomServiceImpl,通过AIDL实现进程间通信,
private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub()
这个需要加下log打印来验证一下代码是不是跑在不同的线程~看一下在Manifest文件里的配置~(经验证,是跑在不同进程或者线程~)
<service android:name=".components.TelecomService"
android:singleUser="true"
android:process="system">
android:process="system" 这个是啥意思?是不是会被系统启动~独立线程?
在TelecomServiceImpl 的placeCall里主要加上了call的权限信息,UserHandle,不知道这个是干啥的。
->new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
callingPackage, hasCallAppOp && hasCallPermission);
->processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);//video state, call permission 的判断处理
-> intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
sendBroadcastToReceiver(intent)
->intent.setClass(mContext, PrimaryCallReceiver.class);
mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
->PrimaryCallReceiver ->getTelecomSystem().getCallIntentProcessor().processIntent(intent);//上面不知道为什么要这么写或者说为什么要这样设计
->CallIntentProcessor ->processOutgoingCallIntent(mContext, mCallsManager, intent); //这里会new一个telecom的call
-》Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
这里的PhoneAccountHandle的不同应该代表的是不同的卡,如果现在有一个正在进行的mForegroundCall,那么这个新的call的PhoneAccountHandle会被设置成和当前这个mForegroundCall的PhoneAccountHandle一样的值。
phoneAccountHandle =
mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme());// 或者通过mPhoneAccountRegistrar来获取默认的电话卡(PhoneAccountHandle),如果没有设置默认电话卡,那这个获取的PhoneAccountHandle可能应该还是为Null的。
call.setTargetPhoneAccount(phoneAccountHandle);
addCall(call);//将新call添加到CallsManager维护的mCalls中
updateCallsManagerState();
updateForegroundCall();// 根据条件来更新当前的mForegroundCall,然后会通过监听这个变化的listener来进行通知,listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
updateCanAddCall();//和上面一样,只不过通知的内容不同,监听者可以在CallsManager构造函数里找到(statusBarNotifier,mRinger,mInCallController...好多~)
-》NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
context, callsManager, call, intent, isPrivilegedDialer);
final int result = broadcaster.processIntent();//还是处理intent,没完没了,不知道这样写的好处是啥。
->NewOutgoingCallIntentBroadcasterprocessIntent() //检查号码是不是voice mail号码什么的
->broadcastIntent(intent, number, !callImmediately) ->mContext.sendOrderedBroadcastAsUser//发广播了
-> 收到广播后mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
false),
mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY));
->call.startCreateConnection(mPhoneAccountRegistrar);
->mCreateConnectionProcessor.process()
->attemptNextPhoneAccount();
下面Telecom和Telephony将通过AIDL来进行通信
ConnectionServiceWrapper service =
mRepository.getService(
phoneAccount.getComponentName(),
phoneAccount.getUserHandle());
service.createConnection(mCall, new Response(service));
->mBinder.bind(callback, call);
mCallbacks.add(callback),isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
这里bindService的对象是Telephony的TelephonyConnectionService,成功后会回调onServiceConnected,
setBinder(binder) //mBinder = binder;把binder保存在Telecom本地,mServiceInterface = IConnectionService.Stub.asInterface(binder);获得TelephonyConnectionService的实例?,mServiceInterface.addConnectionServiceAdapter(adapter); 把自己的adapter给对方,来进行双工通信?。具体能干啥可以看一下这个adapter怎么实现的。
handleSuccessfulConnection() -》callback.onSuccess();//回调刚刚设置好的callback,然后会把mCallbacks清掉
-> mServiceInterface.createConnection //这里会带上call的很多信息, 然后就在Telephony了
3.Telephony
通过bindService,Telecom获得了Telephony的远程实例,Telephony获得了Telecom的adapter,按顺序先看一下拿到adapter做了啥事。
TelephonyConnectionService 会发一条内部的handler消息,MSG_ADD_CONNECTION_SERVICE_ADAPTER
->mAreAccountsInitialized = true; //然后在添加一些连接信息。
之后,Telecom调用了mServiceInterface.createConnection,Telephony ->MSG_CREATE_CONNECTION ->createConnection
这里会根据所带的信息来建立outgoing Connection或者是incoming Connection,拨打电话当然是outgoing。
onCreateOutgoingConnection(callManagerAccount, request)// 这里面还是会详细检查好多条件,号码为null啊,voice mail,CDMA,emergency number,ServiceState,
没啥错误的话 -》placeOutgoingConnection(connection, phone, request); -》originalConnection =
phone.dial(number, null, request.getVideoState(), request.getExtras());
这里的phone是GSMPhone,那么代码又跑到了Framework。
4.Framework~ 在GSMPhone中会处理是否要用Ims(IP Multimedia Subsystem),如果不用IMS,那么直接通过GSMCallTracker 调用RIL的dial接口拨号~。
5.IMS 如果用了IMS,那么会转而调用IMSPhone的dial接口,然后通过ImsPhoneCallTracker的接口来拨号~ synchronized Connection
dial(String dialString, int clirMode, int videoState, Bundle intentExtras)
建立新的ImsPhoneConnection ->mPendingMO = new ImsPhoneConnection(mPhone,
checkForTestEmergencyNumber(dialString), this, mForegroundCall,
isEmergencyNumber); ->dialInternal(mPendingMO, clirMode, videoState, intentExtras);
->ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,callees, mImsCallListener);
conn.setImsCall(imsCall); ->ImsManager.java makeCall
ImsCallSession session = createCallSession(serviceId, profile);
->return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
-->mImsService = IImsService.Stub.asInterface(b); //IImsService.Stub 这个要看各个厂商自己具体实现~
->call.start(session, callees[0]);
ImsCall.java start(ImsCallSession session, String callee)
-->session.start(callee, mCallProfile);
这个session 是 ImsCallSession.java
-> ImsCallSession miSession.start(callee, profile)
这个miSession是远程实例,也应该是厂商自己来实现的。
最终调用的还是RIL的接口dial
如果觉得《Telephony MO CALL/IMS CALL》对你有帮助,请点赞、收藏,并留下你的观点哦!