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

frameworks\opt\telephony\src\java\com\android\internal\telephonytelephoney frameworks frameworks\base\telecomm\java\android\telecomtelecomm frameworkspackages\services\Telecomm\src\com\android\server\telecomtelecom 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 newUnknownConnectionsGsm = new ArrayList();        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 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 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();        ...    }

Android O版本Call对象解析_第1张图片
这里我们可以看到其通过底层返回的 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对象?
Android O版本Call对象解析_第2张图片

参考自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 componentsUpdated = new ArrayList<>();            for (Map.Entry 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进行逐层更新状态

更多相关文章

  1. Android 驱动之旅 第四章:在Android 系统中编写JNI 方法在应用程
  2. 一种绕过Android P对非SDK接口限制的简单方法
  3. Android状态栏和虚拟导航栏的适配总结
  4. Android获取SD卡路径/内存的几种方法
  5. 【Android增量升级系列_02】 浅谈Android增量更新服务端的实现方
  6. WebView之js调用Android类的方法传递数据 - 依凡王子
  7. 〖Android〗简单隐藏Android虚拟键盘的方法

随机推荐

  1. android开发模式LiveData+ViewModel+Room
  2. Android(安卓)SQLiter cursor的使用
  3. Ubuntu android studio 调试 android wea
  4. Warning: License for package Android(
  5. Android(安卓)支持多屏幕机制
  6. Android(安卓)DEX方法超过64K和gradle编
  7. Android(安卓)SystemUI任务栏修改
  8. Configuration on demand is not support
  9. Android(安卓)WebView ScrollBar设置
  10. MySQL: 基于 Android(安卓)远程连接