来电时底层会上报消息到RIL层,在Android8.0上目前已经做了修改,不再使用Socket进行通信,而是使用ril_service服务进行通信,O的系统上引入了HIDL机制,telephony的framework层引用底层的服务就用到了此机制,这也导致了RIL层的调整,本篇博客在7.0的基础上进一步分析其变化,建议同步对照7.0和8.0的代码阅读。
1.我们从RIL层开始往上追溯,RIL接收底层来电消息
来电的消息属于主动上报的UnSolicited消息,其对应的事件ID是CALL_STATE_CHANGE,我们进入RIL.java开始查找。在Android 7.0上的入口是
processUnsolicited方法。
该方法的操作主要操作有以下两点
我们在O版本上只看到了processIndication方法,可以看到该方法是向底层发送确认消息的方法,也就是7.0中processUnsolicited方法的一个操作,可以看到8.0中已经没有processUnsolicited方法,那么RIL是如何接收底层消息的呢?搜索调用此方法的地方。
发现RadioIndication.java有调用此方法。
这里我们看到之前7.0上的processUnsolicited方法对于callState处理的方法移除到这里了。
RadioIndication.java
public void callStateChanged(int indicationType) {
mRil.processIndication(indicationType);
if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
mRil.mCallStateRegistrants.notifyRegistrants();
}
这里的操作和之前一样
注意RadioIndication类
public class RadioIndication extends IRadioIndication.Stub
我们继续看看其继承的IRadioIndication.Stub,全局搜索RadioIndication可以发现其在IRadio.hal中定义了此接口,关于HIDL用法就不再展开,后续有空再补充HIDL的知识。注意RadioIndication类,发现其定义了大量类似callStateChanged的方法,再对比7.0上的processUnsolicited方法,发现本是通过一个方法统一管理的底层回复现在交由RadioIndication类处理,其callStateChanged对应与之前processUnsolicited方法中的switch-case的callStateChanged一项,其他的以此类推。RIL通信的7.0和8.0差别变化比较大的一个地方。接下来继续分析RIL接收到底层消息后,发起的通知传向何处。
2.RIL层在接收到底层的消息后,发起了notify通知,关于regist-notify机制其实就是观察者模式,如果不清楚的可以查看另外一篇博客学习,全局搜索registerForCallStateChanged。发现有三个地方注册了此接口。
其中BascCommands,CommandsInterface都是接口,具体作用可以参见Phone讲解,这里继续跟进GsmCdmaCallTracker。可以看到其继承自CallTracker,本质上是个Handler处理类,所以其关键方法在于handleMessage处理。
3.跟进GsmCdmaCallTracker的handleMessage方法
我们可以看到其在初始化的地方注册了CallStateChanged消息的监听,注册的时候传入了自身的handler,notify的时候使用该handler发送消息,所以我们可以在handleMessage中响应并处理EVENT_CALL_STATE_CHANGE消息。
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
...
case EVENT_CALL_STATE_CHANGE:
pollCallsWhenSafe();
...
4.继续跟踪pollCallsWhenSafe方法
protected void pollCallsWhenSafe() {
mNeedsPoll = true;
if (checkNoOperationsPending()) {
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
mCi.getCurrentCalls(mLastRelevantPoll);
}
}
发现其又调用了mCi.getCurrentCalls去查询当前的Call情况,此方法会调用到RIL中对应的方法去(Phone创建的时候对mCi进行初始化使得具有RIL通信能力,其mCi即调用RIL.java)
5.继续跟进RIL中的getCurrentCalls方法
@Override
public void getCurrentCalls(Message result) {
//区别与7.0的代码,获取一个Radio代理进行通信
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
//创建一个请求消息
RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
}
try {//向底层发起请求
radioProxy.getCurrentCalls(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "getCurrentCalls", e);
}
}
}
6.在发送请求后,等待modem返回结果给RIL层,继续跟踪返回消息的接收
在Android N上RIL请求的返回是在processSolicited方法中处理
其主要操作如下:
那么Android O是如何处理的呢?
在RIL.java中查找底层消息反馈的处理方法,发现有两个方法processRespose和processResponseDone,向上追溯发现其是在RadioResponse.java中的调用此方法。
继续分析RadioResponse类
public class RadioResponse extends IRadioResponse.Stub
发现其继承自一个底层的服务,观察其中各个处理方法发现其中是各中上层请求的返回处理方法。可见N上的processSolicited的消息回复被抽取到这个地方来了。
那么其CurrentCall的消息继续在responseCurrentCalls方法追踪。
public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
ArrayList calls) {
responseCurrentCalls(responseInfo, calls);
}
7.继续追踪调用的responseCurrentCalls方法
private void responseCurrentCalls(RadioResponseInfo responseInfo,
ArrayList calls) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
int num = calls.size();
ArrayList dcCalls = new ArrayList(num);
DriverCall dc;
for (int i = 0; i < num; i++) {
dc = new DriverCall();
// TODO: change name of function stateFromCLCC() in DriverCall.java to name
// clarifying what is CLCC
dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));
dc.index = calls.get(i).index;
dc.TOA = calls.get(i).toa;
dc.isMpty = calls.get(i).isMpty;
dc.isMT = calls.get(i).isMT;
dc.als = calls.get(i).als;
dc.isVoice = calls.get(i).isVoice;
dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;
dc.number = calls.get(i).number;
dc.numberPresentation =
DriverCall.presentationFromCLIP(
(int) (calls.get(i).numberPresentation));
dc.name = calls.get(i).name;
dc.namePresentation =
DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));
if (calls.get(i).uusInfo.size() == 1) {
dc.uusInfo = new UUSInfo();
dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);
dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);
if (!TextUtils.isEmpty(calls.get(i).uusInfo.get(0).uusData)) {
byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();
dc.uusInfo.setUserData(userData);
} else {
mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
}
mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
dc.uusInfo.getType(), dc.uusInfo.getDcs(),
dc.uusInfo.getUserData().length));
mRil.riljLogv("Incoming UUS : data (hex): "
+ IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
} else {
mRil.riljLogv("Incoming UUS : NOT present!");
}
// Make sure there's a leading + on addresses with a TOA of 145
dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
dcCalls.add(dc);
if (dc.isVoicePrivacy) {
mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
mRil.riljLog("InCall VoicePrivacy is enabled");
} else {
mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
mRil.riljLog("InCall VoicePrivacy is disabled");
}
}
Collections.sort(dcCalls);
if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
if (mRil.mEmergencyCallbackModeRegistrant != null) {
mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+ " notify ECM Registrants");
mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
}
}
//发送返回消息
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, dcCalls);
}
mRil.processResponseDone(rr, responseInfo, dcCalls);
}
}
发现其通过底层返回的消息创建了dcCalls对象,也就是当前的Call状态信息,对dc状态进行判断后如果有需要就notify通知,如果没有异常则通过sendMessageResponse方法发送消息
public static void sendMessageResponse(Message msg, Object ret) {
if (msg != null) {
AsyncResult.forMessage(msg, ret, null);
msg.sendToTarget();
}
}
这部分就和N的一样了,将返回值返回给当初的请求者,由请求者去决定如何处理
8.继续回到GsmCdmaCallTracker的handleMessage中,之前发送请求的时候有发送EVENT_POLL_CALLS_RESULT,这里我们继续回到该事件处理的地方来分析
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:
Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
if (msg == mLastRelevantPoll) {
if (DBG_POLL) log(
"handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
mNeedsPoll = false;
mLastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);
}
break;
9.继续追踪handlePollCalls
@Override
protected synchronized void handlePollCalls(AsyncResult ar) {
//解析返回的结果
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < mConnections.length; i++) {
GsmCdmaConnection conn = mConnections[i];
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
if (dc.index == i+1) {
curDC++;
} else {
dc = null;
}
}
...
if (conn == null && dc != null) {
...状态的处理及识别
//响铃消息通知
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
}
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
ArrayList locallyDisconnectedConnections = new ArrayList<>();
for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
//CDMA
boolean wasDisconnected = false;
//来电处理,本地挂断或者未接,本地挂断的话直接设置挂断的原因为LOCAL或INVALID_NUMBER
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected call
int cause;
if (conn.mCause == DisconnectCause.LOCAL) {
cause = DisconnectCause.INCOMING_REJECTED;
} else {
cause = DisconnectCause.INCOMING_MISSED;
}
if (Phone.DEBUG_PHONE) {
log("missed/rejected call, conn.cause=" + conn.mCause);
log("setting cause to " + cause);
}
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(cause);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
} else if (conn.mCause == DisconnectCause.LOCAL
|| conn.mCause == DisconnectCause.INVALID_NUMBER) {
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
}
if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
&& conn == newUnknownConnectionCdma) {
unknownConnectionAppeared = false;
newUnknownConnectionCdma = null;
}
}
if (locallyDisconnectedConnections.size() > 0) {
mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);
}
/* Disconnect any pending Handover connections */
//通话断开的一些处理操作
...
if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
internalClearDisconnected();
}
//更新phone状态
if (VDBG) log("handlePollCalls calling updatePhoneState()");
updatePhoneState();
...
}
可以看到其先对底层反馈的消息进行解析,获取其通话状态,判断如果是来电则发出notifyNewRingingConnection响铃消息通知,然后进行一些通话断开连接的操作及更新phone状态。继续跟进notifyNewRingingConnection响铃消息,该消息调用的是phone的方法,往上追溯。
public void notifyNewRingingConnectionP(Connection cn) {
if (!mIsVoiceCapable)
return;
AsyncResult ar = new AsyncResult(null, cn, null);
mNewRingingConnectionRegistrants.notifyRegistrants(ar);
}
10.继续查看响铃消息通知到何处,全局搜索(registerForNewRingingConnection),发现其监听的主要是以下几个地方
GsmCdmaCallTracker调用的其实是phone的notify方法,所以可以排除PhoneMock.java和Phone.java所以主要继续跟踪的就是CallManager 和 PstnIncommingCallNotifier
看下CallManager的handleMessage消息处理方法
case EVENT_NEW_RINGING_CONNECTION:
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
Connection c = (Connection) ((AsyncResult) msg.obj).result;
int subId = c.getCall().getPhone().getSubId();
if (getActiveFgCallState(subId).isDialing() || hasMoreThanOneRingingCall()) {
try {
Rlog.d(LOG_TAG, "silently drop incoming call: " + c.getCall());
c.getCall().hangup();
} catch (CallStateException e) {
Rlog.w(LOG_TAG, "new ringing connection", e);
}
} else {
mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
}
break;
可以看到CallManager在对此消息进行判断后,确认是否需要挂断,如果不需要则进一步通知此消息。所以其实还是 PstnIncommingCallNotifier 中处理此消息。
通过注释也可发现其是监听来电消息。
/**
* Listens to incoming-call events from the associated phone object and notifies Telecom upon each
* occurence. One instance of these exists for each of the telephony-based call services.
*/
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case EVENT_NEW_RINGING_CONNECTION:
handleNewRingingConnection((AsyncResult) msg.obj);
break;
case EVENT_CDMA_CALL_WAITING:
handleCdmaCallWaiting((AsyncResult) msg.obj);
break;
case EVENT_UNKNOWN_CONNECTION:
handleNewUnknownConnection((AsyncResult) msg.obj);
break;
default:
break;
}
}
继续调用handleNewRingingConnection方法处理调用sendIncommingCallIntent发送Intent,这里其实就已经从RIL传递消息到了应用层了,后续继续分析在应用层如何继续上报的。