Android O版本Call对象解析

在Android8.0中的Call.java有三个地方

frameworks\opt\telephony\src\java\com\android\internal\telephony
telephoney frameworks 

frameworks\base\telecomm\java\android\telecom
telecomm frameworks

packages\services\Telecomm\src\com\android\server\telecom
telecom services

在Android8.0上取消了InCallUI 中的Call

这里我们从telephoney frameworks开始分析

1.telephoney frameworks Call的初始化
在Phone的初始化过程中,会对GsmCdmaCallTraker进行初始化。
GsmCdmaCallTraker过程中,会创建三个GsmCdmaCall对象,并监听CallStateChanged消息

GsmCdmaCall对象继承自telephoney frameworks Call对象

//Call对象
    public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
    // A call that is ringing or (call) waiting
    public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
    public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);

//Connection对象
    public GsmCdmaConnection[] mConnections;

2.telephoney frameworks Call的状态

九种状态,又分为三类
frameworks\opt\telephony\src\java\com\android\internal\telephony

    public enum State {
        IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;

        public boolean isAlive() {
            return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
        }

        public boolean isRinging() {
            return this == INCOMING || this == WAITING;
        }

        public boolean isDialing() {
            return this == DIALING || this == ALERTING;
        }
    }

3.telephoney frameworks Call 状态的获取及更新

当modem的Call状态发生变化后会发送CallStateChanged通知到RIL,继续上报到GsmCdmaCallTracker,之后GsmCdmaCallTracker的hangdleMessage方法处理此消息,
继续调用PollCallsWhenSafe方法,然后下方请求到RIL在到Modem查询GetCurrentCalls,查询的结果主要是DriverCall返回到GsmCdmaCallTracker,接着调用 handlePollCalls 方法进行处理。

具体详细的代码跟踪流程可以见Android8.0 来电分析流程一的1-9步骤

这里我们主要分析handlePollCalls是如何利用查询返回的DriverCall集合更新Call的状态

    @Override
    protected synchronized void handlePollCalls(AsyncResult ar) {
        List polledCalls;
        if (ar.exception == null) {
        //获取返回的数据
            polledCalls = (List)ar.result;
        } 
        Connection newRinging = null; //or waiting
        ArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>();
        Connection newUnknownConnectionCdma = null;
        boolean hasNonHangupStateChanged = false;   // Any change besides
                                                    // a dropped connection
        boolean hasAnyCallDisconnected = false;
        boolean needsPollDelay = false;
        boolean unknownConnectionAppeared = false;
        int handoverConnectionsSize = mHandoverConnections.size();

        //CDMA
        boolean noConnectionExists = true;

        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);
                //返回数据dc的下标比conn的下标大一,可以搜索AT< +CLCC其通话下标是从1开始计数,而conn则是从零开始
                //只要满足dc.index == i+1 的条件,则当前conn的对象是由dc对象创建的,只要dc发生了变化,conn也需要做对应的调整
                if (dc.index == i+1) {
                    curDC++;
                } else {
                    dc = null;
                }
            }


            //conn可以看作是老的通话连接的基本信息,dc可以看作是新的通话连接的基本信息
            //出现新的通话连接
            //老通话conn不存在,出现新通话dc信息
            if (conn == null && dc != null) {
                // Connection appeared in CLCC response that we don't know about
                //MO
                if (mPendingMO != null && mPendingMO.compareTo(dc)) {

                    if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);

                    // It's our pending mobile originating call
                    mConnections[i] = mPendingMO;
                    mPendingMO.mIndex = i;
                    mPendingMO.update(dc);
                    mPendingMO = null;

                    // Someone has already asked to hangup this call
                    if (mHangupPendingMO) {
                        ...
                        //其他操作导致已经挂断了此通话
                        ...
                    }
                } else {
                    //MT
                    if (Phone.DEBUG_PHONE) {
                        log("pendingMo=" + mPendingMO + ", dc=" + dc);
                    }
                    //通过dc对象创建connection
                    mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);

                    Connection hoConnection = getHoConnection(dc);

                    if (hoConnection != null) {
                        //handover切换
                        // Single Radio Voice Call Continuity (SRVCC) completed
                        mConnections[i].migrateFrom(hoConnection);
                        // Updating connect time for silent redial cases (ex: Calls are transferred
                        // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE)
                        if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE &&
                                hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING &&
                                dc.state == DriverCall.State.ACTIVE) {
                            mConnections[i].onConnectedInOrOut();
                        }

                        mHandoverConnections.remove(hoConnection);

                        if (isPhoneTypeGsm()) {
                            for (Iterator<Connection> it = mHandoverConnections.iterator();
                                 it.hasNext(); ) {
                                Connection c = it.next();
                                Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState);
                                if (c.mPreHandoverState == mConnections[i].getState()) {
                                    it.remove();
                                }
                            }
                        }

                        mPhone.notifyHandoverStateChanged(mConnections[i]);
                    } else {
                        //创建来电Connection
                        // find if the MT call is a new ring or unknown connection
                        newRinging = checkMtFindNewRinging(dc,i);
                        if (newRinging == null) {
                            unknownConnectionAppeared = true;
                            if (isPhoneTypeGsm()) {
                                newUnknownConnectionsGsm.add(mConnections[i]);
                            } else {
                                newUnknownConnectionCdma = mConnections[i];
                            }
                        }
                    }
                }
                //标志置为非挂断状态
                hasNonHangupStateChanged = true;
            } 

            //通话连接断开
            //1.通过mDroppedDuringPoll处理
            //如果掉话了则将其加入到删除通话连接列表中mDroppedDuringPoll.add(cn);
            //2.设置旧的通话连接为null,与dc匹配
            //mConnections[i] = null;
            else if (conn != null && dc == null) {
                if (isPhoneTypeGsm()) {
                    // Connection missing in CLCC response that we were
                    // tracking.

                    mDroppedDuringPoll.add(conn);
                } else {
                    // This case means the RIL has no more active call anymore and
                    // we need to clean up the foregroundCall and ringingCall.
                    // Loop through foreground call connections as
                    // it contains the known logical connections.
                    int count = mForegroundCall.mConnections.size();
                    for (int n = 0; n < count; n++) {
                        if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
                        GsmCdmaConnection cn = (GsmCdmaConnection)mForegroundCall.mConnections.get(n);
                        mDroppedDuringPoll.add(cn);
                    }
                    count = mRingingCall.mConnections.size();
                    // Loop through ringing call connections as
                    // it may contain the known logical connections.
                    for (int n = 0; n < count; n++) {
                        if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
                        GsmCdmaConnection cn = (GsmCdmaConnection)mRingingCall.mConnections.get(n);
                        mDroppedDuringPoll.add(cn);
                    }

                    // Re-start Ecm timer when the connected emergency call ends
                    if (mIsEcmTimerCanceled) {
                        handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER);
                    }
                    // If emergency call is not going through while dialing
                    checkAndEnableDataCallAfterEmergencyCallDropped();
                }
                // Dropped connections are removed from the CallTracker
                // list but kept in the Call list
                mConnections[i] = null;
            }
            //通话连接断开并有新的来电
            //
            else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
                // Connection in CLCC response does not match what
                // we were tracking. Assume dropped call and new call
                //通话连接断开处理
                mDroppedDuringPoll.add(conn);
                mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);

                if (mConnections[i].getCall() == mRingingCall) {
                    //新的来电,初始化newRinging连接
                    newRinging = mConnections[i];
                } // else something strange happened
                hasNonHangupStateChanged = true;
            }

            //通话状态发生变化

            else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
                // Call collision case
                if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) {
                    if (dc.isMT == true) {
                        // Mt call takes precedence than Mo,drops Mo
                        mDroppedDuringPoll.add(conn);
                        // find if the MT call is a new ring or unknown connection
                        newRinging = checkMtFindNewRinging(dc,i);
                        if (newRinging == null) {
                            unknownConnectionAppeared = true;
                            newUnknownConnectionCdma = conn;
                        }
                        checkAndEnableDataCallAfterEmergencyCallDropped();
                    } else {
                        // Call info stored in conn is not consistent with the call info from dc.
                        // We should follow the rule of MT calls taking precedence over MO calls
                        // when there is conflict, so here we drop the call info from dc and
                        // continue to use the call info from conn, and only take a log.

                        if(Build.IS_SHOW_LOG){
                            Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
                        }
                    }
                } else {
                    boolean changed;
                    //通过dc对象更新conn对象,返回状态标志是否已经更新
                    changed = conn.update(dc);

                    hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
                }
            }

            if (REPEAT_POLLING) {
                if (dc != null) {
                    // FIXME with RIL, we should not need this anymore
                    if ((dc.state == DriverCall.State.DIALING
                            /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
                        || (dc.state == DriverCall.State.ALERTING
                            /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
                        || (dc.state == DriverCall.State.INCOMING
                            /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
                        || (dc.state == DriverCall.State.WAITING
                            /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) {
                        // Sometimes there's no unsolicited notification
                        // for state transitions
                        needsPollDelay = true;
                    }
                }
            }
        }

        //收尾工作
        // Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data
        // disabled). This should never happen though.
        if (!isPhoneTypeGsm() && noConnectionExists) {
            checkAndEnableDataCallAfterEmergencyCallDropped();
        }

        // This is the first poll after an ATD.
        // We expect the pending call to appear in the list
        // If it does not, we land here
        if (mPendingMO != null) {
            Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"
                    + mForegroundCall.getState());

            mDroppedDuringPoll.add(mPendingMO);
            mPendingMO = null;
            mHangupPendingMO = false;

            if (!isPhoneTypeGsm()) {
                if( mPendingCallInEcm) {
                    mPendingCallInEcm = false;
                }
                checkAndEnableDataCallAfterEmergencyCallDropped();
            }
        }
        //响铃通知
        if (newRinging != null) {
            mPhone.notifyNewRingingConnection(newRinging);
        }
        //根据mDroppedDuringPoll进行挂断处理
        // 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;

            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;
            }
        }
        ...
        //更新phone状态
        if (VDBG) log("handlePollCalls calling updatePhoneState()");
        updatePhoneState();
        ...
    }

Call状态更新
这里我们可以看到其通过底层返回的 DriverCall 和 Connection进行对比以判断通话状态的更改,然后更改Call的State,最后更改Phone的state,并发出通知.
这里我们分别跟进 GsmCdmaConnection 和 GsmCdmaCall 中。

可以看到GsmCdmaCall中有一组connection对象,并且有attch及相关管理方法
GsmCdmaConnection中有一个GsmCdmaCall mParent 实例。

观察其构造方法,可以看到其在创建的时候会先从dc状态中获取parent对象,在将自身和dc添加到mParent中。

        mParent = parentFromDCState(dc.state);
        mParent.attach(this, dc);

继续跟进parentFromDCState

    protected 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);
        }
    }

可以看到其根据dcState的不同返回不同的GsmCdmaCall实例。从这里也可以看到GsmCdmaCallTracker中三个Call对象对应的状态。

所以状态发生的流程总结如下:
获取底层返回的Dc对象,解析,对比Connection,之后更新Connection对应的parent的Call对象,最后更新Phone状态。

Telecom Services的Call对象

在之前的MO,MT流程的分析中提到过,会先创建一个Call,然后调用startCreateConnection去创建Connection具体可以查看去电流程分析二部分
跟进CallsManager中的createCallForExistingConnection方法

Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
        boolean isDowngradedConference = (connection.getConnectionProperties()
                & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
        ...
        setCallState(call, Call.getStateFromConnectionState(connection.getState()),
                "existing connection");
        ...
}

观察其先调用connection.getState方法,在调用getStateFromConnection,之后进行setCallState完成CallState的设置。在之前来电和去电的分析中我们知道,
此Connection其实是远程调用telephony frameworks的Connection

1.从telephony frameworks的Connection中获取CallState.

    public Call.State getState() {
        Call c;

        c = getCall();

        if (c == null) {
            return Call.State.IDLE;
        } else {
            return c.getState();
        }
    }

这就获取到了telephony的Call是State

2.继续查看Call.getStateFromConnectionState

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_PULLING_CALL:
            return CallState.PULLING;
        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;
}

通过转换获取telecomm services中Call对应的State

3.继续跟进CallsManager中的setCallState方法

    private void setCallState(Call call, int newState, String tag) {
        if (call == null) {
            return;
        }
        int oldState = call.getState();
        Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
                CallState.toString(newState), call);
        if (newState != oldState) {
            // Unfortunately, in the telephony world the radio is king. So if the call notifies
            // us that the call is in a particular state, we allow it even if it doesn't make
            // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
            // TODO: Consider putting a stop to the above and turning CallState
            // into a well-defined state machine.
            // TODO: Define expected state transitions here, and log when an
            // unexpected transition occurs.
            //更新Telecomm Service Call对象的State
            call.setState(newState, tag);
            ...
    }

CallsManager如何管理Call对象?
这里写图片描述

参考自liyonggang的图原文链接

Telecom frameworks的Call对象

在Telecomm frameworks层InCallController来控制Call的更新

public class InCallController extends CallsManagerListenerBase

监听CallsManager消息

跟进onCallStateChanged

    public void onCallStateChanged(Call call, int oldState, int newState) {
        updateCall(call);
    }

跟进updateCall

    private void updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged) {
        if (!mInCallServices.isEmpty()) {
            Log.i(this, "Sending updateCall %s", call);
            List<ComponentName> componentsUpdated = new ArrayList<>();
            for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
                ...
                //获取parcelableCall
                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
                        call,
                        videoProviderChanged /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
                ComponentName componentName = info.getComponentName();
                IInCallService inCallService = entry.getValue();
                componentsUpdated.add(componentName);

                try {
                    //继续调用
                    inCallService.updateCall(parcelableCall);
                } catch (RemoteException ignored) {
                }
            }
            Log.i(this, "Components updated: %s", componentsUpdated);
        }
    }

跟进到inCallService

        public void updateCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
        }

查看mHandler的初始化,发现其是当前类,继续跟进到当前类的handleMessage中

public void handleMessage(Message msg) {
            if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
                return;
            }

            switch (msg.what) {
                ...
                case MSG_ADD_CALL:
                    mPhone.internalAddCall((ParcelableCall) msg.obj);
                    break;
                case MSG_UPDATE_CALL:
                    mPhone.internalUpdateCall((ParcelableCall) msg.obj);
                    break;
发现其调用Phone的internalUpdateCall方法进行更新
    final void internalUpdateCall(ParcelableCall parcelableCall) {
         Call call = mCallByTelecomCallId.get(parcelableCall.getId());
         if (call != null) {
             checkCallTree(parcelableCall);
             call.internalUpdate(parcelableCall, mCallByTelecomCallId);
         }
     }

继续跟进就到了Telecomm Frameworks的Call中了,其利用parcelableCall进行更新

InCallUI 的Call
在O上InCallUI下面已经取消了Call.java
不过我们可以搜索到DialerCall.java,其本质上就是之前的Call.java

  private final Call.Callback mTelecomCallCallback =
      new Call.Callback() {
        @Override
        public void onStateChanged(Call call, int newState) {
          LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
          update();
        }

可以看到其监听了TeleComm 的Call
当其接收到onStateChange后调用update方法

  private void update() {
    Trace.beginSection("Update");
    int oldState = getState();
    // We want to potentially register a video call callback here.
    updateFromTelecomCall();
    ..
  }

所有Call的状态就从Telephony framework->Telecomm Service->TelecommFramework->dialerCall进行逐层更新状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值