第1章 主动拨号流程分析
Java Application应用层《--------》Dialer拨号盘和Phone应用;
Java Frameworks 框架层《--------》Telephony Frameworks层
User Libraries系统运行库层《--------》RIL层
拨打电话的大致流程是:Dialer→TeleComService→TeleComFramework→TeleponyService→TelephonyFramework→RIL
总结:
DialpadFragment.onClick()
DialpadFragment.handleDialButtonPressed()
CallIntentProcessor.processIntent()
CallsManager. startOutgoingCall()
CallsManager.java的placeOutgoingCall()
ConnectionService. createConnection()
TelephonyConnectionService.onCreateOutgoingConnection()
GsmCdmaPhone.dial()--》dialInternal()
RIL.dial()
拨号流程总体时序图分析:
拨号流程时序图1
拨号流程时序图图2
1.1 拨号界面DialtactsActivity的onClick
ActivityManager启动DialtactsActivity的Actiivity拨号盘界面,DialtactsActivity中的onClick方法便是进入拨号盘的代码入口。
在onClick方法中会调用mDialpadFragment.dialButtonPressed()和mDialpadFragment.showDialpadFragment。
1.2 DialpadFragment拨号盘表单
目录是:/packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadFragment.java
如果我们按了拨打电话的按钮就会调用dialButtonPressed();---》handleDialButtonPressed();
1)handleDialButtonPressed():的作用是判断是不是空号,是不是禁播号码;创建intent,并调用DialerUtils. startActivityWithErrorToast()方法。
判断是不是空号,是不是禁播号码
else{正常拨号中创建intent
//调用DialerUtils. startActivityWithErrorToast
图1创建intent,调用DialerUtils的方法
2)CallUtil中
CallUtil.getCallIntent(String number, PhoneAccountHandle accountHandle)--》getCallIntent(number, null, accountHandle)---》getCallIntent(getCallUri(number), callOrigin, accountHandle);--》……--》最终会调用getCallIntent( Uri uri, String callOrigin, PhoneAccountHandle accountHandle, int videoState)
我们看一下getCallUri(),它是将号码封装成uri;且会判断是普通电话流程还是网络电话流程。
图2它是将号码封装成uri
接着看一下最后调用的getCallIntent(),这里intent就创建完了,intent创建完了就会传入CallUtils里面的startActivityWithErrorToast()方法【N4手机】。
图3最终的getCallIntent(),在过程中会将号码封装成uri
3)DialerUtils. startActivityWithErrorToast()
图4会用到CallUtil的startActivityWithErrorToast()
CallUtil的startActivityWithErrorToast()
图5会调用TelecomManager.placeCall()
【getTelecomManager是TelecomManager类型的】
getTelecomManager.placeCall():获取framework的服务。
1.3 Telecommanager
TelecomManager.placeCall():
图7在这里定义了一个ITelecomService类型的service调用TelecomServiceImpl中的placeCall
ITelecomService类型的getTelecomService()方法,通过aidl获取TelecomServiceImpl的代理对象。【telecomServiceImpl中有ITelecomService的定义】
图8得到TelecomServiceImpl的代理对象
TelecomServiceImpl中有ITelecomService的定义。从下方代码可以看出TelecomServiceImpl里的mBinderImpl变量是ITelecomService的具体实现类。
图9 TelecomServiceImpl中定义ITelecomService
TelecomServiceImpl的placeCall()方法:主要作用:电话权限检查,isSelfManaged、hasCallAppOp、hasCallPermission。
在这里
图10电话权限检查
这里创建了一个UserCallIntentProcess对象,并调用其processIntent事件处理。
接下来可以看一下,UserCallIntentProcess对象的processIntent事件:
图11调用processOutgoingCallIntent()
processOutgoingCallIntent():
……
图12 sendIntentToDestination()
内部校验一些拨号区县一级其他操作限制,看是否需要直接弹框拒绝,若通过了最后会调用sendIntentToDestination()—》mContext.sendBroadcastAsUser()发送消息。
图13 sendBroadcastAsUser()发送广播
该广播会直接指定发给PrimaryCallReceiver处理。
1.3 PrimaryCallReceiver
PrimaryCallReceiver的onReceiver处理,接着会调用CallIntentProcessor.processIntent()。
图14PrimaryCallReceiver的onReceiver
CallIntentProcessor.processIntent():如果未知号码是空号就会调用processUnknownCallIntent方法处理;否则调用processOutgoingCallIntent
图15
processOutgoingCallIntent():
图16调用sendNewOutgoingCallIntent()、CallsManager.startOutgoingCall()
调用sendNewOutgoingCallIntent()发送广播:
图17发送广播
NewOutgoingCallIntentBroadcaster.processIntent()
processIntent可处理以下三种actions:
CALL、CALL_PRIVILEGED、CALL_EMERGENCY
对于CALL_EMERGENCY的处理:直接设置变量callImmediately为true,直接呼出电话
1.4 CallsManager.java的startOutgoingCall()
//通过该方法得到一个Call对象
……
//获取当前**的卡列表
//如果只有一张卡,则直接用该卡的账户
//设置当前通话账户
//当卡列表大于1时获取当前应该有那张卡呼出
//双卡**下取通话主卡,无通话主卡则为空
//检查当前是否允许呼出该电话,比如当前已有一通电话正在呼出,这时就不允许再来另一通电话进行呼出。
//是否需要弹出双卡选择框(双卡下没有指定账户呼出非紧急号码,且当前无通话主卡)
//设置call的状态为正在连接状态
//将当前call添加到call列表
图18选择卡进行呼出在1.4.2中会用到NewOutgoingCallIntentBroadcastDone()
1.4.1 addCall()具体实现
addCall()方法中会遍历call的状态变化的观察者,并逐个回调通知,这里的观察者比较多,在CallsManager创建的时候注册监听。
图19 CallsManager创建的时候注册监听
CallsManager的构造函数里面会注册监听
图20构造函数里面注册监听
1.4.2 mInCallController对象
1)mInCallController对象是一个InCallController实例,内部封装了与incallui服务的相关操作,实际上就是一个远程服务代理类,当callsManager添加一路call的时候,回调InCallController的onCallAdded方法。
……
图21最后调用inCallService的addCall
最后调用inCallService的addCall方法,告诉incallui当前添加了一路通话,incallui收到消息后会拉起界面。现在回到刚刚1.4 Callsmanager的startOutgoingCall的结尾。
在成功返回一个call对象之后,会新建一个NewOutgoingCallIntentBroadcast对象,用NewOutgoingCallIntentBroadcast.processIntent()方法处理。
1.5 NewOutgoingCallIntentBroadcast.java
NewOutgoingCallIntentBroadcast里面有parseIntent()方法,在这个方法里面会对三种通话类型进行处理。然后还会调用rewriteCallIntentAction()。
parseIntent()方法主要处理三种类型的call(注释这么说的,实际并没有看到ACTION_CALL_PRIVILEGED,在UserCallIntentProcessor.java的parseIntent中有action_call_privileged):
1.普通call Intent.ACTION_CALL :
普通call任何应用都可以发起,第三方应用拨号都是使用该intent
2.系统call Intent.ACTION_CALL_PRIVILEGED :
系统call只有系统应用才能使用
3.紧急呼叫call Intent.ACTION_CALL_EMERGENCY :
紧急呼叫call 同样只有系统应用才能使用,并且可以在无卡状态下拨
在rewriteCallIntentAction()这里,对于一个Intent.ACTION_CALL_PRIVILEGED的拨号请求,会根据当前号码是否为紧急号码来转化该intent
图24
如果是紧急号码则转换为ACTION_CALL_EMERGENCY,如果不是紧急号码则转换为ACTION_CALL。
1)parseIntent()方法对于ACTION_CALL的处理:
如果当前是紧急号码,会校验调用者是否为系统默认的拨号键盘;如果是则配置变量callImmediately为true,后续直接呼出该电话;如果不是则拉起系统默认拨号键盘,当前方法返回。DisconnectCause.OUTGOING_CANCELED
2)NewOutgoingCallIntentBroadcast的parseIntent()方法对于ACTION_CALL_EMERGENCY的处理:直接callImmediately为true,直接呼出该电话。
综上所述:紧急拨号会直接调用CallsManager的placeOutgoingCall方法后,再进入NewOutgoingCallIntentBroadcast的broadcastIntent方法。
注释:parseIntent()方法中callImmediately为true会调用placeOutgoingCallImmediately(……)---》mCallsManager.placeOutgoingCall();
parseIntent()方法最后部分会调用broadcastIntent()。
看一下broadcastIntent():
图25发送广播
在broadcastIntent这里会发送一个ACTION_NEW_OUTGOING_CALL的广播,对于非紧急拨号会生成一个NewOutgoingCallBroadcastIntentReceiver实例来接收该广播。NewOutgoingCallBroadcastIntentReceiver内部做一些处理,最后还是调用到CallsManager的placeOutgoingCall方法。所以placeOutgoingCall方法是去电的关键方法。
1.6 CallsManager.java的placeOutgoingCall()
1)placeOutgoingCall()
//如果是紧急号码,则取消已指定的通话卡账户,如果是紧急号码或者已经指定通话账户,则创建连接。
//如果当前没有**的卡,则断开此连接
图26该方法内部做了一些设置操作后,确认呼出
2)call的startCreateConnection方法。新建了一个连接请求,然后调用CreateConnectionProcessor.java类的process方法处理。
图27新建一个连接请求,调用process()--》attemptNextPhoneAccount()
CreateConnectionProcessor类的process方法会调用attemptNextPhoneAccount(),接下来我们看一下attemptNextPhoneAccount():
//获取connectionServiceWrapper对象
//已成功获取connectionServiceWrapper对象,创建连接。
图28创建连接
这里的mService是ConnectionServiceWrapper实例,实际上就是一个包装了绑定远程服务的代理类。
3)ConnectionServiceWrapper类的构造方法里面有super(ConnectionService.SERVICE_INTERFACE, ……),这里的ConnectionService.SERVICE_INTERFACE就是“android.telecom.ConnectionService”,也就是他所绑定的远程服务ConnectionService的action。
图29远程服务ConnectionService在1.7中会用到
接图28,ConnectionServiceWrapper类获取mService该对象后会调用createConnection()。在createConnection()方法中有mBinder.bind(callback, call);
}}}
图30
ServiceBinder Binder2.bind()中有 ServiceConnection connection = new ServiceBinderConnection(call)。
ServiceBinderConnection 中有ServiceBinderConnection. onServiceConnected()方法,这个方法里面会有setBinder。
这里的mBinder对象是ConnectionServiceWrapper的父类ServiceBinder里面的一个内部类。封装了绑定远程服务的一些操作,若当前还未绑定服务,则直接调用bingService获取远程服务的aidl接口,成功获取到aidl接口后将其赋值给mServiceInterface。【看setServiceInterface()里面会对mServiceInterface进行赋值操作】
图31
最终不管是初次绑定还是之前已绑定服务,调用mBinder.bind(Callback,call)成功后都会回到CallBack的onSuccess()(看图30),接着调用远程服务的createConnection()。
接下来就是到了远程服务的createConnection实现了。
1.7 telecomFramework处理连接请求
1)看1.6的图28,查找远程的实现类是ConnectionService的匿名内部类
--》MSG_CREATE_CONNECTION消息会进入handleMessage进行消息处理(handleMessage在这里是内部类,在右侧不会有显示,可通过搜索消息的方法找到)
图32消息进入handleMessage进行处理
2)handleMessage进行处理消息最终会进入—》createConnection()方法:
……
图33判断是来电还是去电
这里根据来电或者去电创建不同的connection对象,去电走onCreateOutgoingConnection,这个方法返回null,所以具体是显示由其子类TelephonyConnectionService来进行实现的。来电走onCreateInComingConnection,
1.8 TelephonyConnectionService创建呼出连接
TelephonyConnectionService的onCreateOutgoingConnection()方法:
图34
如果失败则返回错误连接对象failedConnection();如果成功则创建一个TelephonyConnection对象,接下来可以看一下创建的过程。
图35
如果是GSM,则创建GSMConnection;如果是CDMA,则创建CDMAConnection对象,获取到对象connection对象后,图34的onCreateOutgoingConnection()最后会调用placeOutgoingConnection()方法。进入呼叫流程。
placeOutgoingConnection()方法:
图36
这里的 originalConnection = phone.dial(number, null, videoState, extras)就是实际的phone呼出入口,由此进入telephonyFramework模块。
1.9 telephonyFramework处理呼出
1)Cdma和Gsm都由GsmCdmaPhone对象统一处理,接下来看一下dial实现:
……
图37
这里还涉及到一个ImsPhone对象,他是phone的子类,是专门用于处理volte通话的。
2)最后进入dialInternal方法:
图38
dialInternal方法,将号码格式化,去除无效字符,然后调用mCT的dial方法,这里的mCT就是GsmCdmaCallTracker对象。
3)接着我们可以看一下GsmCdmaCallTracker类的dial方法:
……
图39
updatePhoneState():更新通话状态;
mPhone.notifyPreciseCallStateChanged():发起phone状态变化通知;
return mPendingMo:返回通话连接。
这里的mCi是CommandsInterface接口类型的,执行dial呼叫操作,追溯mCi的源头,发现是在创建GsmCdmaPhone里面获取的,而GsmCdmaPhone对象又是由PhoneFactory创建。我们在PhoneFactroy.makeDefaultPhone()里面可以看创建的代码。
4)看一下PhoneFactroy.makeDefaultPhone():
……
……
图40
sCommandsInterfaces的创建实际上是RIL的实例,所以呼叫操作实际上是执行RIL的dial方法。
5)看一下RIL的dial方法:
……
图41接着看一下getRadioProxy
getRadioProxy():
mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]); //获取HIDL的服务并设置setResponseFunctions
图42看一下获取HIDL的服务并设置setResponseFunctions
Dial(通过hidl方式调用RadioImpl.dial)
ril_service.cpp中有setResponseFunctions方法。
PS:
HIDL 接口具有客户端和服务器实现:
• HIDL 接口的客户端实现是指通过在该接口上调用方法来使用该接口的代码
• 服务器实现是指 HIDL 接口的实现,它可接收来自客户端的调用并返回结果
HIDL 要求每个使用 HIDL 编写的接口均必须带有版本编号。HAL 接口一经发布便会被冻结,如果要做任何进一步的更改,都只能在接口的新版本中进行
2.1 RIL与底层交互
到RIL.dial(),在RIL中发起RIL异步请求,再一层一层的返回方法调用,最终回到TelephonyManager.showInCallScreen(实际上是TelecomServiceImpl.showInCallScreen)显示通话界面。
1、/hardware/ril/libril/ril_service.cpp
RadioImpl.dial()--》ril.cpp的addRequestToList()
CALL_ONREQUEST()--》reference-ril.c的onRequest()
2、/hardware/ril/reference-ril/reference-ril.c
onRequest():处理RIL_REQUEST_DIAL—》 requestDial()—》……
onRequest()会处理RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND—》atchannel.c的at_send_command()
requestDial:
3、/hardware/ril/reference-ril/atchannel.c
at_send_command
at_send_command_full
at_send_command_full_nolock
writeline(给modem发送消息)
written = write (s_fd, s + cur, len - cur);// s_fd是modem和rilc的串口