• Android7.0 Phone应用源码分析(四) phone挂断流程分析


    接上篇博文:Android7.0 Phone应用源码分析(三) phone拒接流程分析

    今天我们再来分析一下电话挂断流程

    电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析

    先来看下本地挂断电话的时序图:

     

    步骤1:点击通话界面的挂断按钮,会调用到CallCardPresenter的endCallClicked方法,请看CallCardFragment里挂断按钮的监听事件

    com.android.incallui.CallCardFragment
     public void onViewCreated(View view, Bundle savedInstanceState) {
            ...... 
            ......
             mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
                 @Override
                public void onClick(View v) {
                    getPresenter().endCallClicked();
                }
            });
           
    }
    com.android.incallui.CallCardPresenter
     public void endCallClicked() {
            if (mPrimary == null) {
                return;
            }
    
            Log.i(this, "Disconnecting call: " + mPrimary);
            final String callId = mPrimary.getId();
            mPrimary.setState(Call.State.DISCONNECTING);
            CallList.getInstance().onUpdate(mPrimary);
            TelecomAdapter.getInstance().disconnectCall(callId);
     }

    这里先把Call的状态设置成DISCONNECTING,然后通过CallList更新UI界面,最后继续挂断流程

    步骤2:TelecomAdapter的disconnectCall流程

    com.android.incallui.TelecomAdapter
    void disconnectCall(String callId) {
            android.telecom.Call call = getTelecomCallById(callId);
            if (call != null) {
                call.disconnect();
            } else {
                Log.e(this, "error disconnectCall, call not in call list " + callId);
            }
     }

    通过callid找到对应的Call对象(android.telecom.Call)

    步骤3:调用到framework里Call的disconnect方法

    android.telecom.Call
     public void disconnect() {
            mInCallAdapter.disconnectCall(mTelecomCallId);
     }

    这里mInCallAdapter是android.telecom.InCallAdapter类,与telecom进程通信的代理类

    步骤4:InCallAdapter的disconnectCall方法

    android.telecom.InCallAdapter
     public void disconnectCall(String callId) {
            try {
                mAdapter.disconnectCall(callId);
            } catch (RemoteException e) {
            }
    }

    mAdapter就是incallui与telecom通信的AIDL接口

    步骤5:跨进程调用进入telecom进程,该AIDL接口具体实现类是InCallAdapter,相同的类名不一样的包名

    com.android.server.telecom.InCallAdapter
     public void disconnectCall(String callId) {
            try {
                Log.startSession("ICA.dC", mOwnerComponentName);
                long token = Binder.clearCallingIdentity();
                try {
                    synchronized (mLock) {
                        Log.v(this, "disconnectCall: %s", callId);
                        Call call = mCallIdMapper.getCall(callId);
                        if (call != null) {
                            mCallsManager.disconnectCall(call);
                        } else {
                            Log.w(this, "disconnectCall, unknown call id: %s", callId);
                        }
                    }
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
            } finally {
                Log.endSession();
            }
    }

    这里同样是根据callid取出对应Call(com.android.server.telecom.Call),最后调用CallsManager的disconnectCall方法传入call

    步骤6:CallsManager的disconnectCall方法

    com.android.server.telecom.CallsManager
    public void disconnectCall(Call call) {
            Log.v(this, "disconnectCall %s", call);
    
            if (!mCalls.contains(call)) {
                Log.w(this, "Unknown call (%s) asked to disconnect", call);
            } else {
                mLocallyDisconnectingCalls.add(call);
                call.disconnect();
            }
    }

    将该Call对象加入本地断开Call列表,之后进入Call的disconnect方法

    步骤7:Call的disconnect方法

    com.android.server.telecom.Call 
    public void disconnect() {
            disconnect(false);
     }
    public void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {
            Log.event(this, Log.Events.REQUEST_DISCONNECT);
    
            // Track that the call is now locally disconnecting.
            setLocallyDisconnecting(true);
    
            if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
                    mState == CallState.CONNECTING) {
                Log.v(this, "Aborting call %s", this);
                abort(wasViaNewOutgoingCallBroadcaster);
            } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
                if (mConnectionService == null) {
                    Log.e(this, new Exception(), "disconnect() request on a call without a"
                            + " connection service.");
                } else {
                    Log.i(this, "Send disconnect to connection service for call: %s", this);
                    // The call isn't officially disconnected until the connection service
                    // confirms that the call was actually disconnected. Only then is the
                    // association between call and connection service severed, see
                    // {@link CallsManager#markCallAsDisconnected}.
                    mConnectionService.disconnect(this);
                }
            }
     }

    setLocallyDisconnecting(true); 先设置是否为本地挂断标志为true

    由于mState这时候是CallState.ACTIVE状态,进入mConnectionService的disconnect方法

    这里的mConnectionService是ConnectionServiceWrapper类,是telecom与telephony通信的代理类

    步骤8:ConnectionServiceWrapper的disconnect方法

    com.android.server.telecom.ConnectionServiceWrapper
    void disconnect(Call call) {
            final String callId = mCallIdMapper.getCallId(call);
            if (callId != null && isServiceValid("disconnect")) {
                try {
                    logOutgoing("disconnect %s", callId);
                    mServiceInterface.disconnect(callId);
                } catch (RemoteException e) {
                }
            }
    }

    这里mServiceInterface就是telephony提供给telecom调用的AIDL接口

    步骤9:跨进程调用进入telephony进程,AIDL接口具体实现是其父类ConnectionService的mBinder成员变量

    android.telecom.ConnectionService
     private final IBinder mBinder = new IConnectionService.Stub() {
           
            ...... 
            ......
    
    public void disconnect(String callId) {
                mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
            }
    
    }

    步骤10~13:发送MSG_DISCONNECT消息到队列里处理

    private void disconnect(String callId) {
            Log.d(this, "disconnect %s", callId);
            if (mConnectionById.containsKey(callId)) {
                findConnectionForAction(callId, "disconnect").onDisconnect();
            } else {
                findConferenceForAction(callId, "disconnect").onDisconnect();
            }
        }
    private Connection findConnectionForAction(String callId, String action) {
            if (mConnectionById.containsKey(callId)) {
                return mConnectionById.get(callId);
            }
            Log.w(this, "%s - Cannot find Connection %s", action, callId);
            return getNullConnection();
        }

    根据callid找到对应的connection对象(android.telecom.Connection),调用onDisconnect方法

    步骤14,15:TelephonyConnection继承于connection

    com.android.services.telephony.TelephonyConnection
     public void onDisconnect() {
            Log.v(this, "onDisconnect");
            hangup(android.telephony.DisconnectCause.LOCAL);
        }
    protected void hangup(int telephonyDisconnectCode) {
            if (mOriginalConnection != null) {
                try {
                    // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
                    // connection.hangup(). Without this change, the party originating the call will not
                    // get sent to voicemail if the user opts to reject the call.
                    if (isValidRingingCall()) {
                        Call call = getCall();
                        if (call != null) {
                            call.hangup();
                        } else {
                            Log.w(this, "Attempting to hangup a connection without backing call.");
                        }
                    } else {
                        // We still prefer to call connection.hangup() for non-ringing calls in order
                        // to support hanging-up specific calls within a conference call. If we invoked
                        // call.hangup() while in a conference, we would end up hanging up the entire
                        // conference call instead of the specific connection.
                        mOriginalConnection.hangup();
                    }
                } catch (CallStateException e) {
                    Log.e(this, e, "Call to Connection.hangup failed with exception");
                }
            }
        }

    设置断开连接类型为DisconnectCause.LOCAL,并调用mOriginalConnection的hangup方法, 实际Connection类是GsmCdmaConnection

    步骤16:GsmCdmaConnection的hangup方法

    com.android.internal.telephony.GsmCdmaConnection
     public void hangup() throws CallStateException {
            if (!mDisconnected) {
                mOwner.hangup(this);
            } else {
                throw new CallStateException ("disconnected");
            }
     }

    这里的mOwner对想是GsmCdmaCallTracker类型

    步骤17:GsmCdmaCallTracker的hangup方法

    com.android.internal.telephony.GsmCdmaCallTracker
     public void hangup(GsmCdmaConnection conn) throws CallStateException {
            if (conn.mOwner != this) {
                throw new CallStateException ("GsmCdmaConnection " + conn
                                        + "does not belong to GsmCdmaCallTracker " + this);
            }
    
            if (conn == mPendingMO) {
                // We're hanging up an outgoing call that doesn't have it's
                // GsmCdma index assigned yet
    
                if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
                mHangupPendingMO = true;
            } else if (!isPhoneTypeGsm()
                    && conn.getCall() == mRingingCall
                    && mRingingCall.getState() == GsmCdmaCall.State.WAITING) {
                // Handle call waiting hang up case.
                //
                // The ringingCall state will change to IDLE in GsmCdmaCall.detach
                // if the ringing call connection size is 0. We don't specifically
                // set the ringing call state to IDLE here to avoid a race condition
                // where a new call waiting could get a hang up from an old call
                // waiting ringingCall.
                //
                // PhoneApp does the call log itself since only PhoneApp knows
                // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
                // is not called here. Instead, conn.onLocalDisconnect() is called.
                conn.onLocalDisconnect();
    
                updatePhoneState();
                mPhone.notifyPreciseCallStateChanged();
                return;
            } else {
                try {
                    mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());
                } catch (CallStateException ex) {
                    // Ignore "connection not found"
                    // Call may have hung up already
                    Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection "
                                    + conn);
                }
            }
    
            conn.onHangupLocal();
     }

    由于是通话中挂断,这里调用 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());

    这里调用conn的getGsmCdmaIndex方法先获取索引

    com.android.internal.telephony.GsmCdmaConnection
     getGsmCdmaIndex() throws CallStateException {
            if (mIndex >= 0) {
                return mIndex + 1;
            } else {
                throw new CallStateException ("GsmCdma index not yet assigned");
            }
    }

    这个索引对应的就是DriveCall里的index值,匹配modem里CallList里对于Call对象

    mCi是CommandsInterface即RILJ接口,包装了一个EVENT_OPERATION_COMPLETE回调消息,发送给RIL

    步骤18:RIL的hangupConnection方法

    com.android.internal.telephony.RIL
    public void  hangupConnection (int gsmIndex, Message result) {
            if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);
    
            RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);
    
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
                    gsmIndex);
    
            mEventLog.writeRilHangup(rr.mSerial, RIL_REQUEST_HANGUP, gsmIndex);
    
            rr.mParcel.writeInt(1);
            rr.mParcel.writeInt(gsmIndex);
    
            send(rr);
     }

    给RIL层发送RIL_REQUEST_HANGUP消息并附带index参数

    步骤19~23:收到RIL层的回应消息并处理,最后发送回调消息EVENT_OPERATION_COMPLETE给GsmCdmaCallTracker

    步骤24:GsmCdmaCallTracker处理回调消息EVENT_OPERATION_COMPLETE

    com.android.internal.telephony.GsmCdmaCallTracker 
    private void operationComplete() {
            mPendingOperations--;
    
            if (DBG_POLL) log("operationComplete: pendingOperations=" +
                    mPendingOperations + ", needsPoll=" + mNeedsPoll);
    
            if (mPendingOperations == 0 && mNeedsPoll) {
                mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
                mCi.getCurrentCalls(mLastRelevantPoll);
            } else if (mPendingOperations < 0) {
                // this should never happen
                Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");
                mPendingOperations = 0;
            }
        }

    这里再次向RIL发送消息主动获取当前Call状态,包装的回调消息为EVENT_POLL_CALLS_RESULT

    步骤25~31:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息并处理

    com.android.internal.telephony.GsmCdmaCallTracker 
    protected synchronized void handlePollCalls(AsyncResult ar) {
            List polledCalls;
    
            if (VDBG) log("handlePollCalls");
            if (ar.exception == null) {
                polledCalls = (List)ar.result;
            } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
                // just a dummy empty ArrayList to cause the loop
                // to hang up all the calls
                polledCalls = new ArrayList();
            } else {
                // Radio probably wasn't ready--try again in a bit
                // But don't keep polling if the channel is closed
                pollCallsAfterDelay();
                return;
            }
    
            …………
    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;
                    }
                }
    
              …………          
                } else if (conn != null && dc == null) {
                    if (isPhoneTypeGsm()) {
                        // Connection missing in CLCC response that we were
                        // tracking.
                        mDroppedDuringPoll.add(conn);
                        // Dropped connections are removed from the CallTracker
                        // list but kept in the GsmCdmaCall list
                        mConnections[i] = null;
                    } else {
                    
    
               …………      
            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;
                } else if (conn.mCause == DisconnectCause.LOCAL
                        || conn.mCause == DisconnectCause.INVALID_NUMBER) {
                    mDroppedDuringPoll.remove(i);
                    hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
                    wasDisconnected = true;
                }
    
                if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
                        && conn == newUnknownConnectionCdma) {
                    unknownConnectionAppeared = false;
                    newUnknownConnectionCdma = null;
                }
            }
    
            /* Disconnect any pending Handover connections */
            for (Iterator<Connection> it = mHandoverConnections.iterator();
                    it.hasNext();) {
                Connection hoConnection = it.next();
                log("handlePollCalls - disconnect hoConn= " + hoConnection +
                        " hoConn.State= " + hoConnection.getState());
                if (hoConnection.getState().isRinging()) {
                    hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED);
                } else {
                    hoConnection.onDisconnect(DisconnectCause.NOT_VALID);
                }
                it.remove();
            }
    
            // Any non-local disconnects: determine cause
            if (mDroppedDuringPoll.size() > 0) {
                mCi.getLastCallFailCause(
                    obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
            }
    
             …………
            if (VDBG) log("handlePollCalls calling updatePhoneState()");
            updatePhoneState();
    
            if (unknownConnectionAppeared) {
                if (isPhoneTypeGsm()) {
                    for (Connection c : newUnknownConnectionsGsm) {
                        log("Notify unknown for " + c);
                        mPhone.notifyUnknownConnection(c);
                    }
                } else {
                    mPhone.notifyUnknownConnection(newUnknownConnectionCdma);
                }
            }
    
            if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
                mPhone.notifyPreciseCallStateChanged();
            }
    
            //dumpState();
        }
    }

    这里是通话断开事件,将connection放入mDroppedDuringPoll列表,由于断开类型是DisconnectCause.LOCAL

    直接调用GsmCdmaConnection的onDisconnect方法传入cause参数

    步骤32:GsmCdmaConnection的onDisconnect方法

    com.android.internal.telephony.GsmCdmaConnection
     public boolean onDisconnect(int cause) {
            boolean changed = false;
    
            mCause = cause;
    
            if (!mDisconnected) {
                doDisconnect();
    
                if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
    
                mOwner.getPhone().notifyDisconnect(this);
    
                if (mParent != null) {
                    changed = mParent.connectionDisconnected(this);
                }
    
                mOrigConnection = null;
            }
            clearPostDialListeners();
            releaseWakeLock();
            return changed;
      }

    doDisconnect方法设置断开时间以及通话时长

    private void  doDisconnect() {
            mIndex = -1;
            mDisconnectTime = System.currentTimeMillis();
            mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
            mDisconnected = true;
            clearPostDialListeners();
    }

    最后通知注册者断开事件mOwner.getPhone().notifyDisconnect(this);

    步骤33之后的流程就跟上篇讲解phone拒接流程一样,这里就不重复描述了

    详见http://www.cnblogs.com/lance2016/p/6391096.html

    讲完本地挂断电话的流程,再讲一下远程挂断的流程,先看一下相关的时序图

     

    步骤1~4:modem层上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED事件,RIL通知EVENT_CALL_STATE_CHANGE消息

    步骤5,6,7:GsmCdmaCallTracker处理消息,向RIL发送消息主动获取当前Call状态,包装的回调消息为EVENT_POLL_CALLS_RESULT

    com.android.internal.telephony.CallTracker 
    protected void pollCallsWhenSafe() {
            mNeedsPoll = true;
    
            if (checkNoOperationsPending()) {
                mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
                mCi.getCurrentCalls(mLastRelevantPoll);
            }
    }

    步骤8~13:RIL返回消息,GsmCdmaCallTracker接收EVENT_POLL_CALLS_RESULT消息并处理

    com.android.internal.telephony.GsmCdmaCallTracker
     protected synchronized void handlePollCalls(AsyncResult ar) {
            List polledCalls;
    
           …………
    if (mDroppedDuringPoll.size() > 0) {
                mCi.getLastCallFailCause(
                    obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
            }
    
           …………
    }

    这里跟前面本地挂断的流程差不多,但区别是由于不是本地挂断的cause类型,会主动再向RIL发送消息获取通话断开的cause

    包装的回调消息为EVENT_GET_LAST_CALL_FAIL_CAUSE

    步骤14:RIL的getLastCallFailCause方法

    com.android.internal.telephony.RIL
     public void getLastCallFailCause (Message result) {
            RILRequest rr
                    = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result);
    
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
    
            send(rr);
     }

    向MODEM发送RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息

    步骤15~20:modem回应RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息,RIL发送回调消息EVENT_GET_LAST_CALL_FAIL_CAUSE

    步骤21:GsmCdmaCallTracker处理消息

    com.android.internal.telephony.GsmCdmaCallTracker
     case EVENT_GET_LAST_CALL_FAIL_CAUSE:
                    int causeCode;
                    String vendorCause = null;
                    ar = (AsyncResult)msg.obj;
    
                    operationComplete();
    
                    if (ar.exception != null) {
                        // An exception occurred...just treat the disconnect
                        // cause as "normal"
                        causeCode = CallFailCause.NORMAL_CLEARING;
                        Rlog.i(LOG_TAG,
                                "Exception during getLastCallFailCause, assuming normal disconnect");
                    } else {
                        LastCallFailCause failCause = (LastCallFailCause)ar.result;
                        causeCode = failCause.causeCode;
                        vendorCause = failCause.vendorCause;
                    }
                    // Log the causeCode if its not normal
                    if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
                        causeCode == CallFailCause.TEMPORARY_FAILURE ||
                        causeCode == CallFailCause.SWITCHING_CONGESTION ||
                        causeCode == CallFailCause.CHANNEL_NOT_AVAIL ||
                        causeCode == CallFailCause.QOS_NOT_AVAIL ||
                        causeCode == CallFailCause.BEARER_NOT_AVAIL ||
                        causeCode == CallFailCause.ERROR_UNSPECIFIED) {
    
                        CellLocation loc = mPhone.getCellLocation();
                        int cid = -1;
                        if (loc != null) {
                            if (isPhoneTypeGsm()) {
                                cid = ((GsmCellLocation)loc).getCid();
                            } else {
                                cid = ((CdmaCellLocation)loc).getBaseStationId();
                            }
                        }
                        EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid,
                                TelephonyManager.getDefault().getNetworkType());
                    }
    
                    for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) {
                        GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
    
                        conn.onRemoteDisconnect(causeCode, vendorCause);
                    }
    
                    updatePhoneState();
    
                    mPhone.notifyPreciseCallStateChanged();
                    mDroppedDuringPoll.clear();
                break;

    RIL在处理RIL_REQUEST_LAST_CALL_FAIL_CAUSE消息时将结果放入LastCallFailCause对象,传递给GsmCdmaCallTracker

    GsmCdmaCallTracker得到结果,其中causeCode值就是断开原因,接着调用GsmCdmaConnection的onRemoteDisconnect方法

    步骤22:GsmCdmaConnection的onRemoteDisconnect方法

    com.android.internal.telephony.GsmCdmaConnection
    void onRemoteDisconnect(int causeCode, String vendorCause) {
            this.mPreciseCause = causeCode;
            this.mVendorCause = vendorCause;
            onDisconnect(disconnectCauseFromCode(causeCode));
        }

    这里通过disconnectCauseFromCode方法将causeCode值转化为DisconnectCause值

    int disconnectCauseFromCode(int causeCode) {
            /**
             * See 22.001 Annex F.4 for mapping of cause codes
             * to local tones
             */
    
            switch (causeCode) {
                case CallFailCause.USER_BUSY:
                    return DisconnectCause.BUSY;
    
                case CallFailCause.NO_CIRCUIT_AVAIL:
                case CallFailCause.TEMPORARY_FAILURE:
                case CallFailCause.SWITCHING_CONGESTION:
                case CallFailCause.CHANNEL_NOT_AVAIL:
                case CallFailCause.QOS_NOT_AVAIL:
                case CallFailCause.BEARER_NOT_AVAIL:
                    return DisconnectCause.CONGESTION;
    
                case CallFailCause.ACM_LIMIT_EXCEEDED:
                    return DisconnectCause.LIMIT_EXCEEDED;
    
                case CallFailCause.CALL_BARRED:
                    return DisconnectCause.CALL_BARRED;
    
                case CallFailCause.FDN_BLOCKED:
                    return DisconnectCause.FDN_BLOCKED;
    
                case CallFailCause.UNOBTAINABLE_NUMBER:
                    return DisconnectCause.UNOBTAINABLE_NUMBER;
    
                case CallFailCause.DIAL_MODIFIED_TO_USSD:
                    return DisconnectCause.DIAL_MODIFIED_TO_USSD;
    
                case CallFailCause.DIAL_MODIFIED_TO_SS:
                    return DisconnectCause.DIAL_MODIFIED_TO_SS;
    
                case CallFailCause.DIAL_MODIFIED_TO_DIAL:
                    return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
    
                case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
                    return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE;
    
                case CallFailCause.CDMA_DROP:
                    return DisconnectCause.CDMA_DROP;
    
                case CallFailCause.CDMA_INTERCEPT:
                    return DisconnectCause.CDMA_INTERCEPT;
    
                case CallFailCause.CDMA_REORDER:
                    return DisconnectCause.CDMA_REORDER;
    
                case CallFailCause.CDMA_SO_REJECT:
                    return DisconnectCause.CDMA_SO_REJECT;
    
                case CallFailCause.CDMA_RETRY_ORDER:
                    return DisconnectCause.CDMA_RETRY_ORDER;
    
                case CallFailCause.CDMA_ACCESS_FAILURE:
                    return DisconnectCause.CDMA_ACCESS_FAILURE;
    
                case CallFailCause.CDMA_PREEMPTED:
                    return DisconnectCause.CDMA_PREEMPTED;
    
                case CallFailCause.CDMA_NOT_EMERGENCY:
                    return DisconnectCause.CDMA_NOT_EMERGENCY;
    
                case CallFailCause.CDMA_ACCESS_BLOCKED:
                    return DisconnectCause.CDMA_ACCESS_BLOCKED;
    
                case CallFailCause.ERROR_UNSPECIFIED:
                case CallFailCause.NORMAL_CLEARING:
                default:
                    GsmCdmaPhone phone = mOwner.getPhone();
                    int serviceState = phone.getServiceState().getState();
                    UiccCardApplication cardApp = phone.getUiccCardApplication();
                    AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
                                                                AppState.APPSTATE_UNKNOWN;
                    if (serviceState == ServiceState.STATE_POWER_OFF) {
                        return DisconnectCause.POWER_OFF;
                    } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
                            || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) {
                        return DisconnectCause.OUT_OF_SERVICE;
                    } else {
                        if (isPhoneTypeGsm()) {
                            if (uiccAppState != AppState.APPSTATE_READY) {
                                return DisconnectCause.ICC_ERROR;
                            } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
                                if (phone.mSST.mRestrictedState.isCsRestricted()) {
                                    return DisconnectCause.CS_RESTRICTED;
                                } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
                                    return DisconnectCause.CS_RESTRICTED_EMERGENCY;
                                } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
                                    return DisconnectCause.CS_RESTRICTED_NORMAL;
                                } else {
                                    return DisconnectCause.ERROR_UNSPECIFIED;
                                }
                            } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
                                return DisconnectCause.NORMAL;
                            } else {
                                // If nothing else matches, report unknown call drop reason
                                // to app, not NORMAL call end.
                                return DisconnectCause.ERROR_UNSPECIFIED;
                            }
                        } else {
                            if (phone.mCdmaSubscriptionSource ==
                                    CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM
                                    && uiccAppState != AppState.APPSTATE_READY) {
                                return DisconnectCause.ICC_ERROR;
                            } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
                                return DisconnectCause.NORMAL;
                            } else {
                                return DisconnectCause.ERROR_UNSPECIFIED;
                            }
                        }
                    }
            }
        }

    由于causeCode值是NORMAL_CLEARING,所以得到的DisconnectCause值是DisconnectCause.NORMAL

    接着调用onDisconnect方法

    public boolean onDisconnect(int cause) {
            boolean changed = false;
    
            mCause = cause;
    
            if (!mDisconnected) {
                doDisconnect();
    
                if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
    
                mOwner.getPhone().notifyDisconnect(this);
    
                if (mParent != null) {
                    changed = mParent.connectionDisconnected(this);
                }
    
                mOrigConnection = null;
            }
            clearPostDialListeners();
            releaseWakeLock();
            return changed;
        }

    步骤23之后的流程就跟上篇讲解phone拒接流程一样,这里就不重复描述了

    详见http://www.cnblogs.com/lance2016/p/6391096.html

    至此,一个电话挂断的流程就分析完了,结合log打印会对整个流程的理解更加深刻

  • 相关阅读:
    面向对象基础
    VmWare下安装CentOS6图文安装教程
    设计模式培训之一:为什么要用单例模式?
    CentOS5.4下安装和配置Apache、PHP、MySql、PHPMyAdmin
    WEB架构师成长系列索引
    WEB架构师成长之路之三架构师都要懂哪些知识
    设计模式培训之三:抽象工厂
    IOS6屏幕自动旋转设置测试
    设计模式培训之二:简单工厂、工厂方法
    QT和Oracle连接的oci驱动的编译
  • 原文地址:https://www.cnblogs.com/lance2016/p/6582858.html
Copyright © 2020-2023  润新知