来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程中 Telephony 直接与Moderm层交互, com.android.phone 进程收到来来电消息后,发送消息给 system 进程, system 进程(Telecom作为中介)开始和com.android.phone 进程建立链接, 并通知 UI 进程 (com.android.dialer) 更新。大体上和拨号过程类似。
来电流程时序图:
对于MO Call来说,一般是由用户自己操作来发起的主动动作,可以根据UI上的button来跟踪流程。但是对于MTcall来说,一般是被动的接收modem的消息,不太好从UI的层面来跟踪流程,所以大概的总结下MT流程。
首先,查来电消息的处理。在来电时,首先是由modem向上上报来电的消息,上层来处理。第一条消息是:RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,消息报上来后,到RadioIndication.java来处理(在Android O后,UNSOL消息在RadioIndication处理,SOL消息在RadioResponse里处理。替代了之前的processUnsolicited & processSolicited方法)。这条消息就是modem 告知上层:当前Call的状态发送变化了。但是上层并不知道真正的变化是什么,所以Tele回去询问当前Call到底是属于什么状态,发出请求:RIL_REQUEST_GET_CURRENT_CALLS。当Modem得到这些请求后,会将这些信息返回给Tele.
从RIL层开始往上追溯,RIL接收底层来电消息。来电的消息属于主动上报的UnSolicited消息,其对应的事件ID是CALL_STATE_CHANGE,我们进入RIL.java开始查找。
UNSOL_RESPONSE_CALL STATE CHANGED状态改变
@UnsupportedAppUsage
5692 void unsljLog(int response) {
5693 riljLog("[UNSL]< " + responseToString(response));
5694 }
@UnsupportedAppUsage
5561 static String responseToString(int request) {
5562 switch(request) {
5563 case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
5564 return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
5565 case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
5566 return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
5567 case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED:
RadioIndication 类有17 个方法调用RIL 对象的unsljLog 方法打印消息,处理此
RIL 消息的代码逻辑详情如下:
public void callStateChanged(int indicationType) {
mRil.processIndication(indicationType);
if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
mRil.mCallStateRegistrants.notifyRegistrants();
}
- 调用processIndication向底层发送确认收到消息
- notifyRegistrant发出通知
RIL层在接收到底层的消息后,发起了notify通知。----------- 观测者模式
其中:
public class RadioIndication extends IRadioIndication.Stub {
而 mCallStateRegistrants 在:
RIL继承于父类 BaseCommands ,实现了 mCallStateRegistrants
public class RIL extends BaseCommands implements CommandsInterface
protected RegistrantList mCallStateRegistrants = new RegistrantList();
此处是: registerForCallStateChanged
搜索到有:
- BaseCommands
- GsmCdmaCallTracker
- CommandsInterface
其中BascCommands,CommandsInterface都是接口
mCalIStateReg istrants. notifyRegistrant 发出通知后,仅一个 地方可响应此消息通知,即GsmCdmaCallTracker 类的handleMessage 方法,本质上是个Handler处理类
跟进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();
...
pollCallsWhenSafe 调用父类CallTracker 方法
protected void pollCallsWhenSafe() {
mNeedsPoll = true;
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
mCi.getCurrentCalls(mLastRelevantPoll);
}
}
发现其又调用了mCi.getCurrentCalls去查询当前的Call情况,此方法会调用到RIL中对应的方法去(Phone创建的时候对mCi进行初始化使得具有RIL通信能力,其mCi即调用RIL.java)
又回到了 RIL 中
@Override
public void getCurrentCalls(Message result) {
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);
}
}
}
在发送请求后,等待modem返回结果给RIL层,继续跟踪返回消息的接收(省去 与 RIL 层的交互)
在RIL.java中查找底层消息反馈的处理方法,发现有两个方法processRespose和processResponseDone,向上追溯发现其是在RadioResponse.java中的
responseCurrentCalls方法 调用了 processResponseDone 方法,如下:
其继承自一个底层的服务
public class RadioResponse extends IRadioResponse.Stub
CurrentCall的消息继续在responseCurrentCalls方法追踪。
public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
ArrayList<android.hardware.radio.V1_0.Call> calls) {
responseCurrentCalls(responseInfo, calls);
}
继续追踪调用的responseCurrentCalls方法
private void responseCurrentCalls(RadioResponseInfo responseInfo,
ArrayList<android.hardware.radio.V1_0.Call> calls) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
int num = calls.size();
ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(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();
}
}
继续回到GsmCdmaCallTracker的handleMessage中,之前发送请求的时候有发送EVENT_POLL_CALLS_RESULT,这里我们继续回到该事件处理的地方来分析
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
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;
继续追踪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<GsmCdmaConnection> 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) {