Android N Call 状态分析

来源:互联网 发布:如何退出淘宝客 编辑:程序博客网 时间:2024/06/03 12:45

本流程图基于MTK平台 Android 7.0,普通电话,本流程只作为沟通学习使用

通过前面关于 MO 和 MT 的分析和学习,我们大致了解了整个Phone的两个主要流程,今天我们要了解的是整个流程中 Call 的状态是如何变化的。这里有参考到 4.4 的状态分析,有些区别。

DriverCall.State

当 modem 发生状态改变时,它会通过 RILC 和 RILJ 将状态上报到我们 framework 层,接收并转换这些状态的正是我们的 DriverCall。

源码分析

不管是MO还是MT流程,我们都会执行 RIL.responseCallList 这里会调用 DriverCall.stateFromCLCC 方法,如下:

//这里将 modem 传上来的状态进行转换    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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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)
  • 1
  • 2
  • 3

通过 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>在电话本中的数字表示
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们 DriverCall.State 转换的规则就是根据上面 “stat” 字段的描述来将 modem 的状态转换到 framework 层的状态。

GsmCdmaCall.call

然后会调用到 GsmCdmaCallTracker.handlePollCalls 方法,这里会通过判断是 MO 还是 MT 来得到 call 的三种类型:

//GsmCdmaCallTracker.handlePollCalls                // Connection appeared in CLCC response that we don't know about                if (mPendingMO != null && mPendingMO.compareTo(dc)) {//MO的时候.....省略部分代码                    mPendingMO.mIndex = i;                    mPendingMO.update(dc);//通过 DriverCall 的状态来确定 GsmCdmaCall 的类型                    mPendingMO = null;.....省略部分代码                } else { //MT的时候                    if (Phone.DEBUG_PHONE) {                        log("pendingMo=" + mPendingMO + ", dc=" + dc);                    }                    /// M: CC: Remove handling for MO/MT conflict, not hangup MT @{                    if (mPendingMO != null && !mPendingMO.compareTo(dc)) {                        log("MO/MT conflict! MO should be hangup by MD");                    }                    /// @}                    mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);//通过创建连接来确定当前 GsmCdmaCall 的类型//GsmCdmaConnection.parentFromDCState 获得 GsmCdmaCall 类型的具体方法    private GsmCdmaCall parentFromDCState (DriverCall.State state) {        switch (state) {            case ACTIVE:            case DIALING:            case ALERTING:                return mOwner.mForegroundCall;            //break;            case HOLDING:                return mOwner.mBackgroundCall;            //break;            case INCOMING:            case WAITING:                return mOwner.mRingingCall;            //break;            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);

//GsmCdmaCall.attach    public void attach(Connection conn, DriverCall dc) {        mConnections.add(conn);        mState = stateFromDCState (dc.state); //获得call当前的状态,根据DriverCall 的状态    }//frameworks/opt/telephone   Call.stateFromDCState 具体分类实现    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) 状态来决定的。

//GsmCdmaCallTracker.updatePhoneState    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();            /// M: ALPS02192901. @{            // If the call is disconnected after CIREPH=1, before +CLCC, the original state is            // idle and new state is still idle, so callEndCleanupHandOverCallIfAny isn't called.            // Related CR: ALPS02015368, ALPS02161020, ALPS02192901.            // if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){            if (imsPhone != null) {                /// @}                imsPhone.callEndCleanupHandOverCallIfAny();            }            mState = PhoneConstants.State.IDLE;//设置状态        }.....省略部分代码    }//frameworks/opt/telephone   Call.java        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;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

当三方应用通过调用 getCallState 的时候就是返回的 TelephonyManager 的CALL_STATE_RINGING、CALL_STATE_OFFHOOK、CALL_STATE_IDLE 这三种状态:

//三方app 调用方式TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); tm.getCallState();  //源代码 frameworks/base/telephony  TelephonyManager.java    /**     * 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:                    /// M: CC: ECC Retry @{                    // Assume only one ECC exists                    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:                    /// M: CC: ECC Retry @{                    // Assume only one ECC exists                    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:                    /// M: CC: ECC Retry @{                    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 :表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

CallState(services/Telecomm)

当 connection 创建完成后就会创建与之相对应的 call,这里 call 的状态是通过 packages/services/Telecom 下面的 call.getStateFromConnectionState 方法得到的,它是根据前面得到的 connection 状态来设置的。

//设置call(services/Telecomm)的状态    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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Call.State(base/telecomm)

当call发生了一些改变后,我们会将 Call.State(base/telecomm) 的状态封装一下并转换成 Call.State(base/telecomm) 的状态,以备提供内 dialer 应用使用,相应的转换代码如下:

//ParcelableCallUtils.getParcelableState    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 we are marked as 'locally disconnecting' then mark ourselves as disconnecting instead.        // Unless we're disconnect*ED*, in which case leave it at that.        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 : 表示一个连接正处于从远端连接拉到本地连接的一个状态(比如有两个设备但是共用一个号码)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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界面时候的中间状态
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

小结

通过上面的转换,我们就一步一步将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 的状态进行分类包装并传递
原创粉丝点击