本流程图基于MTK平台 Android 7.0,普通电话,本流程只作为沟通学习使用
通过前面关于 MO 和 MT 的分析和学习,我们大致了解了整个Phone的两个主要流程,今天我们要了解的是整个流程中 Call 的状态是如何变化的。这里有参考到 4.4 的状态分析,有些区别。
DriverCall.State
当 modem 发生状态改变时,它会通过 RILC 和 RILJ 将状态上报到我们 framework 层,接收并转换这些状态的正是我们的 DriverCall。
源码分析
不管是MO还是MT流程,我们都会执行 RIL.responseCallList 这里会调用 DriverCall.stateFromCLCC 方法,如下:
public static State stateFromCLCC(int state) throws ATParseEx { switch(state) { case 0: return State.ACTIVE; case 1: return State.HOLDING; case 2: return State.DIALING; case 3: return State.ALERTING; case 4: return State.INCOMING; case 5: return State.WAITING; default: throw new ATParseEx("illegal call state " + state); } }
modem状态分析
通过底层反馈的log信息如下:
//AT 指令 Line 13952: 01-23 15:04:54.777 I/AT ( 868): AT> AT+CLCC (RIL_CMD_READER_2, tid:512639079504) Line 13959: 01-23 15:04:54.784 I/AT ( 868): AT< +CLCC: 1,0,3,0,0,"10010",129 (RIL_CMD_READER_2, tid:512639079504)
通过 AT< +CLCC 我们看到modem给我们反馈的信息,其代表的含义如下:
2.7.6 AT+CLCC 列举当前的电话 该命令返回当前电话的列表 命令格式AT+CLCC 响 应 OK 如果当前没有电话 +CLCC: <id1>, <dir>, <stat>, <mode>, <mpty> [ ,<number>, <type> [ <alpha> ] ] <idx> 整数类型电话识别码 <dir> 0 移动台发起MO的电话 1 移动台终止MT的电话 <stat> 电话的状态 0 正在迚行 1 保持 2 拨号MO 3 振铃MO 4 来电MT 5 等待MT <mode> 0语音 1数据 2传真 3语音(语音跟随数据) 4语音(语音数据交换) 5语音(语音传真交换) 6数据(语音跟随数据) 7数据(数据语音交换) 8传真(语音传真交换) 9 未知 <mpty> 0 电话不是多方会话中的成员 1 电话是多方会话中的成员 <number> 字符类型的电话号码格式通过<type>指定 <type> 129没有国际接入码“+” 145有国际接入码“+” <alpha> <number>在电话本中的数字表示
我们 DriverCall.State 转换的规则就是根据上面 “stat” 字段的描述来将 modem 的状态转换到 framework 层的状态。
GsmCdmaCall.call
然后会调用到 GsmCdmaCallTracker.handlePollCalls 方法,这里会通过判断是 MO 还是 MT 来得到 call 的三种类型:
if (mPendingMO != null && mPendingMO.compareTo(dc)) {.....省略部分代码 mPendingMO.mIndex = i; mPendingMO.update(dc); mPendingMO = null;.....省略部分代码 } else { if (Phone.DEBUG_PHONE) { log("pendingMo=" + mPendingMO + ", dc=" + dc); } if (mPendingMO != null && !mPendingMO.compareTo(dc)) { log("MO/MT conflict! MO should be hangup by MD"); } mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i); private GsmCdmaCall parentFromDCState (DriverCall.State state) { switch (state) { case ACTIVE: case DIALING: case ALERTING: return mOwner.mForegroundCall; case HOLDING: return mOwner.mBackgroundCall; case INCOMING: case WAITING: return mOwner.mRingingCall; default: throw new RuntimeException("illegal call state: " + state); } }.....省略部分代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
Call.State(opt/telephony)
在对 GsmCdmaCall 进行分类后就会对 call 的状态进行分类,这里的 call 是指 frameworks-opt-telephony 下面的 call, 不管是 update 还是创建 connection 后都会执行 mParent.attach(this, dc);
public void attach(Connection conn, DriverCall dc) { mConnections.add(conn); mState = stateFromDCState (dc.state); } public static State stateFromDCState (DriverCall.State dcState) { switch (dcState) { case ACTIVE: return State.ACTIVE; case HOLDING: return State.HOLDING; case DIALING: return State.DIALING; case ALERTING: return State.ALERTING; case INCOMING: return State.INCOMING; case WAITING: return State.WAITING; default: throw new RuntimeException ("illegal call state:" + dcState); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
小结:
通过上面的分类,我们就成功的将 modem 传上来的状态进行了分类,分成了不同的 call 类型和 call 状态,它们的关系如下图
向三方暴露call的状态
PhoneConstants状态
在 GsmCdmaCallTracker.handlePollCalls 的后半部分会执行 updatePhoneState 方法,这个方法会决定 PhoneConstants 的状态,它的状态是根据上面的 Call.State(opt/telephony) 状态来决定的。
private void updatePhoneState() { PhoneConstants.State oldState = mState; if (mRingingCall.isRinging()) { mState = PhoneConstants.State.RINGING; } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { mState = PhoneConstants.State.OFFHOOK; } else { Phone imsPhone = mPhone.getImsPhone(); if (imsPhone != null) { imsPhone.callEndCleanupHandOverCallIfAny(); } mState = PhoneConstants.State.IDLE; }.....省略部分代码 } public boolean isRinging() { return this == INCOMING || this == WAITING; } public boolean isAlive() { return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING); } public boolean isDialing() { return this == DIALING || this == ALERTING; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
TelephonyManager 中 call 的状态
上面装换成 PhoneConstants 状态后会执行 mPhone.notifyPhoneStateChanged(); 方法,通过父类层层调用,最总会调用到 DefaultPhoneNotifier.notifyPhoneState 方法,最后调用 convertCallState 方法将状态转换成 TelephonyManager 中call 的状态,如下代码:
/** * Convert the {@link PhoneConstants.State} enum into the TelephonyManager.CALL_STATE_* * constants for the public API. */ public static int convertCallState(PhoneConstants.State state) { switch (state) { case RINGING: return TelephonyManager.CALL_STATE_RINGING; case OFFHOOK: return TelephonyManager.CALL_STATE_OFFHOOK; default: return TelephonyManager.CALL_STATE_IDLE; } }
当三方应用通过调用 getCallState 的时候就是返回的 TelephonyManager 的CALL_STATE_RINGING、CALL_STATE_OFFHOOK、CALL_STATE_IDLE 这三种状态:
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); tm.getCallState(); /** * Returns one of the following constants that represents the current state of all * phone calls. * * {@link TelephonyManager#CALL_STATE_RINGING} * {@link TelephonyManager#CALL_STATE_OFFHOOK} * {@link TelephonyManager#CALL_STATE_IDLE} */ public int getCallState() { try { ITelecomService telecom = getTelecomService(); if (telecom != null) { return telecom.getCallState(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getCallState", e); } return CALL_STATE_IDLE; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
小结:
到此处我们就将 framework 层 call 的状态暴露给了三方应用,它们的对应关系如下图:
内部call不同层次的对应关系
Connection.State(base/telecomm)
当创建 connection 的时候会执行 TelephonyConnection.updateStateInternal 方法,来设置connection 的状态,它的大部分状态也是根据 Call.State(opt/telephony) 的状态来设置的。
void updateStateInternal() {......省略部分代码 switch (newState) { case IDLE: break; case ACTIVE: if (mTreatAsEmergencyCall && TelephonyConnectionServiceUtil.getInstance().isEccRetryOn()) { Log.d(this, "ECC Retry : clear ECC param"); TelephonyConnectionServiceUtil.getInstance().clearEccRetryParams(); } setActiveInternal(); break; case HOLDING: setOnHold(); break; case DIALING: case ALERTING: setDialing(); break; case INCOMING: case WAITING: setRinging(); break; case DISCONNECTED: if (mTreatAsEmergencyCall && TelephonyConnectionServiceUtil.getInstance().isEccRetryOn()) { Log.d(this, "ECC Retry : clear ECC param"); TelephonyConnectionServiceUtil.getInstance().clearEccRetryParams(); } setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( mOriginalConnection.getDisconnectCause(), mOriginalConnection.getVendorDisconnectCause())); close(); break; case DISCONNECTING: mIsLocallyDisconnecting = true; break; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
通过上面的方法就可以根据 Call.State(opt/telephony) 设置 Connection 的大部分状态,包括:STATE_RINGING、STATE_DIALING、STATE_ACTIVE、STATE_HOLDING、STATE_DISCONNECTED,其余状态的转换条件如下:
STATE_INITIALIZING : 连接正在初始化状态,创建一个连接的时候会设置这个状态,它是连接的第一个状态 STATE_NEW :是一个新的连接,但是还没连接上,一般判断为创建一个紧急号码的connection的时候会设置成这个状态 STATE_RINGING : 一个来电连接,此时手机处于ringing状态,震动并响铃 STATE_DIALING : 一个处于外拨的连接,此时对方还没有应答,可以听到嘟嘟的声音 STATE_ACTIVE :一个连接处于活动状态,双方可以正常主动通信 STATE_HOLDING : 一个连接存于hold状态 STATE_DISCONNECTED : 一个断开连接,这个是连接的最终状态, STATE_PULLING_CALL :表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码)
CallState(services/Telecomm)
当 connection 创建完成后就会创建与之相对应的 call,这里 call 的状态是通过 packages/services/Telecom 下面的 call.getStateFromConnectionState 方法得到的,它是根据前面得到的 connection 状态来设置的。
static int getStateFromConnectionState(int state) { switch (state) { case Connection.STATE_INITIALIZING: return CallState.CONNECTING; case Connection.STATE_ACTIVE: return CallState.ACTIVE; case Connection.STATE_DIALING: return CallState.DIALING; case Connection.STATE_DISCONNECTED: return CallState.DISCONNECTED; case Connection.STATE_HOLDING: return CallState.ON_HOLD; case Connection.STATE_NEW: return CallState.NEW; case Connection.STATE_RINGING: return CallState.RINGING; } return CallState.DISCONNECTED; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
相关状态说明:
NEW : 表面当前的call是新的并且没有被连接上,在telecom这个是call的默认初始状态 CONNECTING : 外拨call的初始状态成功后会转换成DIALING状态,失败后会转换成DISCONNECTED状态 SELECT_PHONE_ACCOUNT : 如果有多个phoneaccount会在外拨时出现这个状态,询问用户选择哪个账户拨打电话 DIALING :表明给一个call正处于dialing状态,如果对方接收会转换成ACTIVE状态,取消或者拒接会转换成DISCONNECTED RINGING : call处于来电状态,接听转换成ACTIVE 否则转成DISCONNECTED ACTIVE : call 以及接通,双方可以正常通话 ON_HOLD :call没有终止,但是不能相互通信,一般有ACTIVE转换得来 DISCONNECTED :当前的call已经断开连接 ABORTED : 这个call尝试创建连接,但在成功之前被主动取消掉了 DISCONNECTING :当前call正在断开连接,断开后会转入DISCONNECTED
Call.State(base/telecomm)
当call发生了一些改变后,我们会将 Call.State(base/telecomm) 的状态封装一下并转换成 Call.State(base/telecomm) 的状态,以备提供内 dialer 应用使用,相应的转换代码如下:
private static int getParcelableState(Call call) { int state = CallState.NEW; switch (call.getState()) { case CallState.ABORTED: case CallState.DISCONNECTED: state = android.telecom.Call.STATE_DISCONNECTED; break; case CallState.ACTIVE: state = android.telecom.Call.STATE_ACTIVE; break; case CallState.CONNECTING: state = android.telecom.Call.STATE_CONNECTING; break; case CallState.DIALING: state = android.telecom.Call.STATE_DIALING; break; case CallState.DISCONNECTING: state = android.telecom.Call.STATE_DISCONNECTING; break; case CallState.NEW: state = android.telecom.Call.STATE_NEW; break; case CallState.ON_HOLD: state = android.telecom.Call.STATE_HOLDING; break; case CallState.RINGING: state = android.telecom.Call.STATE_RINGING; break; case CallState.SELECT_PHONE_ACCOUNT: state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT; break; } if (call.isLocallyDisconnecting() && (state != android.telecom.Call.STATE_DISCONNECTED)) { state = android.telecom.Call.STATE_DISCONNECTING; } return state; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
相关状态说明
STATE_NEW : 新创建的call的状态 STATE_DIALING : call当前正在外拨但是还没有连接上 STATE_RINGING : call当前正处于来电状态,但是没有连接上 STATE_HOLDING : call当前处于hold状态 STATE_ACTIVE : call处于活动状态支持互相通话 STATE_DISCONNECTED : 当前的call释放了所有资源处于断开连接状态 STATE_SELECT_PHONE_ACCOUNT : 等待用户选择phoneaccount STATE_PRE_DIAL_WAIT :也是等待用户选择phoneaccount STATE_CONNECTING : 外拨的初始状态,如果成功会转换成STATE_DIALING 否则转换成STATE_DISCONNECTED STATE_DISCONNECTING : 正处于断开连接状态 STATE_PULLING_CALL : 表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码)
Cal.State(Dialer/incallUI)
在incallUI中我们想分辨不同 call 的状态,就需要将上面 telecom 中的Call.State(base/telecomm) 转换成 dialer 中的 Cal.State(Dialer/incallUI),相关的转换代码如下:
private static int translateState(int state) { switch (state) { case android.telecom.Call.STATE_NEW: case android.telecom.Call.STATE_CONNECTING: return Call.State.CONNECTING case android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT: return Call.State.SELECT_PHONE_ACCOUNT case android.telecom.Call.STATE_DIALING: return Call.State.DIALING case android.telecom.Call.STATE_RINGING: return Call.State.INCOMING case android.telecom.Call.STATE_ACTIVE: return Call.State.ACTIVE case android.telecom.Call.STATE_HOLDING: return Call.State.ONHOLD case android.telecom.Call.STATE_DISCONNECTED: return Call.State.DISCONNECTED case android.telecom.Call.STATE_DISCONNECTING: return Call.State.DISCONNECTING default: return Call.State.INVALID } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
相关状态说明
INVALID : call 初始化mState变量时会用到,没什么具体的含义 NEW : call 是新建的 IDLE : call处于空闲状态 CALL_WAITING : 来电,但是当前有个正处于activity状态的call REDIALING : 拨号失败后再次尝试拨号 CONFERENCED : 会议电话中的一个call CONNECTING : 外拨电话,dialer等待Telecom 的广播call状态发生改变完成 BLOCKED :当前的call在黑名单列表中 WAIT_ACCOUNT_RESPONSE : call状态指明现在正等待账户相应,在选择phoneaccount界面时候的中间状态
小结
通过上面的转换,我们就一步一步将modem下面反馈上来的 call 状态通过 DriverCall–telephony–telecom–dialer/incallUI ,从而和我们上层界面的 call 状态一一对应,他们的对应关系如下图:
总结
- 当 SystemServer 起来的时候就会通过 ActivityThread 去创建 PhoneApp,PhoneApp会调用PhoneGlobals 通过 PhoneFactory.makeDefaultPhones(this);创建相关的 phone 出来,在创建phone的时候会通过 int numPhones =TelephonyManager.getDefault().getPhoneCount();去得到创建几个 phone 和 RIL,最终是通过去读取 static final String PROPERTY_MULTI_SIM_CONFIG =”persist.radio.multisim.config”; 这个系统属性的值来判断的。这个属性值就包括DSDS(Dual SIMDual Standby 双卡双待单通 :两个radio但是同一时间只能存在一个,它们会快速的来回切换)、DSDA(DSDA - Dual SIM DualActive双卡双待双通:两个radio并且可以同时存在并使用)、TSTS(TSTS - Triple SIM Triple Standby三卡三待)等
- 创建完 phone 之后就会去创建三种类型的 call,包括:mForegroundCall、mBackgroundCall和 mRingingCall
- 对 call 分完类之后就会去创建 connection,协议规定一个 call 最多包含5个 connection(会议通话一次最多5个connection),mBackgroundCall 和 mRingingCall 都只包含一个connection
- connection 创建完成之后就会对 call 的状态进行分类包装并传递