Android 5.1 Phone 挂断电话流程分析

写在前面的话


本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理。
挂断电话主要分两种情况:


一、 本地主动挂断电话

(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)


这里说的本地主动挂断电话,是指通过点击UI界面上的挂断按钮来挂断电话,而不是通过物理键来挂断电话;至于通过物理键挂断电话,第二节再分析。
挂断电话的按钮floating_end_call_action_button是在call_card_content.xml布局文件里的,该布局文件是在CallCardFragment.java的onCreateView方法里被加载,因此,本地主动挂断电话的流程就从CallCardFragment.java开始。


http://blog.csdn.net/linyongan


步骤1:在CallCardFragment.java的onViewCreated()方法里可以找到,挂断电话的按钮floating_end_call_action_button所绑定的监听器,当挂断按钮被点击时就会执行监听器里的方法。

@Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ...
        mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getPresenter().endCallClicked();
            }
        });
        ...
}

步骤2: CallCardPresenter.java的CallCardPresenter()方法

   public void endCallClicked() {
        if (mPrimary == null) {
            return;
        }

        Log.i(this, "Disconnecting call: " + mPrimary);
        mPrimary.setState(Call.State.DISCONNECTING);
        Call temp = mPrimary;
        CallList.getInstance().onUpdate(mPrimary);
        TelecomAdapter.getInstance().disconnectCall(temp.getId());
   }

在这里做了3件事:
1、把Call的状态设置成DISCONNECTING。(步骤3)
2、更新UI界面,显示正在挂断的界面。(步骤4,5,7,8)
3、继续走挂断电话的流程。(步骤6)

步骤6: TelecomAdapter.java的disconnectCall()方法

    void disconnectCall(String callId) {
        if (mPhone != null) {
            getTelecommCallById(callId).disconnect();
        } else {
            Log.e(this, "error disconnectCall, mPhone is null");
        }
    }

    private android.telecom.Call getTelecommCallById(String callId) {
        final Call call = CallList.getInstance().getCallById(callId);
        return call == null ? null : call.getTelecommCall();
    }

在getTelecommCallById()方法里通过Package目录下InCallUI Call来间接获取到framework目录下的Telecomm Call,Telecomm Call是在InCallUI Call实例创建时传递进来的。

步骤9: (frameworks\base\telecomm\java\android\telecom目录下)Call.java的disconnect()方法

    /**
     * Instructs this {@code Call} to disconnect.
     */
    public void disconnect() {
        mInCallAdapter.disconnectCall(mTelecomCallId);
    }

mInCallAdapter是InCallAdapter类型的。

步骤10: (frameworks\base\telecomm\java\android\telecom目录下)InCallAdapter.java的disconnectCall()方法

    /**
     * Instructs Telecom to disconnect the specified call.
     *
     * @param callId The identifier of the call to disconnect.
     */
    public void disconnectCall(String callId) {
        try {
            mAdapter.disconnectCall(callId);
        } catch (RemoteException e) {
        }
    }

mAdapter是IInCallAdapter类型的。

步骤11,12和13: (packages\services\telecomm\src\com\android\server\telecom目录下)InCallAdapter.java的disconnectCall()方法

    @Override
    public void disconnectCall(String callId) {
        Log.v(this, "disconnectCall: %s", callId);
        if (mCallIdMapper.isValidCallId(callId)) {
            mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
        }
    }

在这里通过obtainMessage()方法创建了一个消息类型为MSG_DISCONNECT_CALL的Message,再调用sendToTarget()方法把Message发送出去。

步骤14: InCallAdapter.java内部类InCallAdapterHandler的handleMessage()方法

    private final class InCallAdapterHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Call call;
            switch (msg.what) {
            ...
             case MSG_DISCONNECT_CALL:
                    call = mCallIdMapper.getCall(msg.obj);
                    if (call != null) {
                        mCallsManager.disconnectCall(call);
                    } else {
                        Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
                    }
                    break;
            ...
   }

步骤15: CallsManager.java的disconnectCall()方法

    /**
     * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
     * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
     * the user hitting the end-call button.
     */
    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();
        }
    }

步骤16和17: (packages\services\telecomm\src\com\android\server\telecom目录下)Call.java的disconnect()方法

   /**
     * Attempts to disconnect the call through the connection service.
     */
    void disconnect() {
        disconnectWithReason(DisconnectCause.LOCAL);
    }

    void disconnectWithReason(int disconnectCause) {
        // Track that the call is now locally disconnecting.
        Log.d(this, "disconnectWithReason dicsonnectcause %d", disconnectCause);
        setLocallyDisconnecting(true);

        if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT ||
                mState == CallState.CONNECTING) {
            Log.d(this, "Aborting call %s", this);
            abort();
        } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
            Log.d(this, "disconnectWithReason %s", mConnectionService);
            if (mConnectionService == null) {
                Log.e(this, new Exception(), "disconnect() request on a call without a"
                        + " connection service.");
            } else {
                Log.d(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.disconnectWithReason(this, disconnectCause);
            }
        }
    }

disconnect()方法里把挂断电话的原因设置成DisconnectCause.LOCAL。还有就是mConnectionService是ConnectionServiceWrapper类型的。

步骤18: ConnectionServiceWrapper.java的disconnectWithReason()方法

    void disconnectWithReason(Call call, int disconnectCause) {
        final String callId = mCallIdMapper.getCallId(call);
        if (callId != null && isServiceValid("disconnect")) {
            try {
                logOutgoing("disconnect %s", callId);
                mServiceInterface.disconnectWithReason(callId, disconnectCause);
            } catch (RemoteException e) {
            }
        }
    }

mServiceInterface是IConnectionService类型的。

步骤19,20和21: ConnectionService.java里mBinder的disconnectWithReason()方法

    private final IBinder mBinder = new IConnectionService.Stub() {
        ...
        @Override
        public void disconnectWithReason(String callId, int disconnectCause) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = callId;
            args.argi1 = disconnectCause;
            mHandler.obtainMessage(MSG_DISCONNECT_REASON, args).sendToTarget();
        }
        ...
    }

在这里通过obtainMessage()方法创建了一个消息类型为MSG_DISCONNECT_REASON的Message,并且通过sendToTarget()方法把Message发送出去。

步骤22: ConnectionService.java里mHandler的handleMessage()方法

 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            ...
            case MSG_DISCONNECT_REASON: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    try {
                        String callId = (String) args.arg1;
                        int disconnectCause = args.argi1;
                        disconnectWithReason(callId, disconnectCause);
                    } finally {
                        args.recycle();
                    }
                    break;
                }
            ...
   }

步骤23: ConnectionService.java的disconnectWithReason()方法

    private void disconnectWithReason(String callId, int disconnectCause) {
        Log.d(this, "disconnectWithReason %s %d DisconnectCause", callId, disconnectCause);
        if (mConnectionById.containsKey(callId)) {
            findConnectionForAction(callId, "disconnect").onDisconnectWithReason(disconnectCause);
        } else {
            findConferenceForAction(callId, "disconnect").onDisconnectWithReason(disconnectCause);
        }
   }

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

    static synchronized Connection getNullConnection() {
        if (sNullConnection == null) {
            sNullConnection = new Connection() {};
        }
        return sNullConnection;
    }

通过findConnectionForAction获得一个Connection实例,在这里获得的是TelephonyConnection。

步骤24和25: TelephonyConnection.java的disconnectWithReason()方法

    @Override
    public void onDisconnectWithReason(int disconnectCause) {
        Log.v(this, "onDisconnect");
        hangup(DisconnectCauseUtil.toTelephonyDisconnectCauseCode(disconnectCause));
    }

    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.hangupWithReason(telephonyDisconnectCode);
                    } 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.hangupWithReason(telephonyDisconnectCode);
                }
            } catch (CallStateException e) {
                Log.e(this, e, "Call to Connection.hangup failed with exception");
            }
        }
    }

步骤26和27: CDMAConnection.java的hangupWithReason()方法

    @Override
    public void hangupWithReason(int disconnectCause) throws CallStateException {
        //TODO not passing disconnect cause in CS.
        hangup();
    }

    @Override
    public void hangup() throws CallStateException {
        if (!mDisconnected) {
            mOwner.hangup(this);
        } else {
            throw new CallStateException ("disconnected");
        }
    }

注意,此时mOwner.hangup(this);传递的参数是this,也就是CdmaConnection类型的。

步骤28和29: CdmaCallTracker.java的hangup()方法

   //***** Called from CdmaConnection
    /*package*/ void
    hangup (CdmaConnection conn) throws CallStateException {
        if (conn.mOwner != this) {
            throw new CallStateException ("CdmaConnection " + conn
                                    + "does not belong to CdmaCallTracker " + this);
        }

        if (conn == mPendingMO) {
            // We're hanging up an outgoing call that doesn't have it's
            // GSM index assigned yet

            if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
            mHangupPendingMO = true;
        } else if ((conn.getCall() == mRingingCall)
                && (mRingingCall.getState() == CdmaCall.State.WAITING)) {
            // Handle call waiting hang up case.
            //
            // The ringingCall state will change to IDLE in CdmaCall.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.getCDMAIndex(), obtainCompleteMessage());
            } catch (CallStateException ex) {
                // Ignore "connection not found"
                // Call may have hung up already
                Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
                                + conn);
            }
        }

        conn.onHangupLocal();
    }

    /**
     * Obtain a message to use for signalling "invoke getCurrentCalls() when
     * this operation and all other pending operations are complete
     */
    private Message
    obtainCompleteMessage() {
        return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
    }

通过obtainCompleteMessage()创建了一个消息类型为EVENT_OPERATION_COMPLETE的Message作为mCi.hangupConnection的一个参数,其中mCi是RIL.java的实例。

步骤30: RIL.java的hangupConnection()方法

    @Override
    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);

        rr.mParcel.writeInt(1);
        rr.mParcel.writeInt(gsmIndex);

        send(rr);
    }

RILJ封装了一个RIL_REQUEST_HANGUP类型的消息,并且通过send(rr);发送给RILD,RILD继续处理。
步骤31和32: 当RILJ接收到RILD的回应时,在它的processResponse()方法有RIL_REQUEST_HANGUP相关的逻辑处理,最后将消息发送给rr. result对应的handler处理(rr. result就是hangupConnection传递进来的Message参数),即由CdmaCallTracker中的handleMessage()方法进行处理。

步骤33~35: CdmaCallTracker.java的handleMessage()方法

 public void
    handleMessage (Message msg) {
        ...
        switch (msg.what) {
        case EVENT_OPERATION_COMPLETE:
                operationComplete();
        break;
        ...
   }

   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,"CdmaCallTracker.pendingOperations < 0");
            mPendingOperations = 0;
        }
    }

通过obtainMessage()创建了一个消息类型为EVENT_POLL_CALLS_RESULT的Message,然后传递给mCi.getCurrentCalls()

步骤36,37和38:调用RIL.java的getCurrentCalls()方法发出查询Call List状态列表的请求,底层查询完之后返回的结果还是交给CdmaCallTracker中的handleMessage()方法进行处理。

步骤39和40: CdmaCallTracker.java的handleMessage()方法

public void
    handleMessage (Message msg) {
        ...
        switch (msg.what) {
        case EVENT_POLL_CALLS_RESULT:{
                Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
                ar = (AsyncResult)msg.obj;

                if(msg == mLastRelevantPoll) {
                    if(DBG_POLL) log(
                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
                    mNeedsPoll = false;
                    mLastRelevantPoll = null;
                    handlePollCalls((AsyncResult)msg.obj);
                }
            }
        break;
        ...
   }

    @Override
    protected void
    handlePollCalls(AsyncResult ar) {
            // clear the "local hangup" and "missed/rejected call"
        // cases from the "dropped during poll" list
        // These cases need no "last call fail" reason
        for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
            CdmaConnection conn = mDroppedDuringPoll.get(i);

            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);
            } else if (conn.mCause == DisconnectCause.LOCAL
                    || conn.mCause == DisconnectCause.INVALID_NUMBER) {
                mDroppedDuringPoll.remove(i);
                hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
            }
        }
}

关于handlePollCalls()方法更详细的讲解请看《handlePollCalls方法详解
在步骤16就可以知道电话挂断的原因是DisconnectCause.LOCAL,因此下一步会调用conn.onDisconnect(conn.mCause)

步骤41: CDMAConnection.java的onDisconnect()方法

   /**
     * Called when the radio indicates the connection has been disconnected.
     * @param cause call disconnect cause; values are defined in {@link DisconnectCause}
     */
    /*package*/ boolean
    onDisconnect(int cause) {
        boolean changed = false;
        mCause = cause;
        if (!mDisconnected) {
            doDisconnect();
            if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
            mOwner.mPhone.notifyDisconnect(this);
            if (mParent != null) {
                changed = mParent.connectionDisconnected(this);
            }
        }
        releaseWakeLock();
        return changed;
    }

步骤42和43:这里主要通过mPhone.notifyDisconnect(this);通知监听了这个事件的人。
最后发觉是注册TelephonyConnection.java监听了MSG_DISCONNECT事件

getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);

因此,在TelephonyConnection.java里会有MSG_DISCONNECT相应的逻辑处理。

步骤44和45: TelephonyConnection.java里mHandler的handleMessage()方法

 private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            ...
            case MSG_DISCONNECT:
                    updateState();
                    break;
            ...
            }
 }
 void updateState() {
        ...
        if (mOriginalConnectionState != newState) {
            mOriginalConnectionState = newState;
            switch (newState) {
                case IDLE:
                    break;
                case ACTIVE:
                    setActiveInternal();
                    break;
                case HOLDING:
                    setOnHold();
                    break;
                case DIALING:
                case ALERTING:
                    setDialing();
                    break;
                case INCOMING:
                case WAITING:
                    setRinging();
                    break;
                case DISCONNECTED:
                    if (mSsNotification != null) {
                        setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                                mOriginalConnection.getDisconnectCause(),
                                mSsNotification.notificationType,
                                mSsNotification.code));
                        mSsNotification = null;
                        DisconnectCauseUtil.mNotificationCode = 0xFF;
                        DisconnectCauseUtil.mNotificationType = 0xFF;
                    } 
                    resetDisconnectCause();
                    close();
                    break;
                ...
            }
        }
        updateConnectionCapabilities();
        updateAddress();
    }

当前的State是DISCONNECTED,所以会执行Connection.java的setDisconnected方法;close();也会继续走一段流程,就是把当前Call对象从Call集合里remove掉,这段流程暂且不分析了。

步骤46: TelephonyConnection.java里mHandler的handleMessage()方法

    /**
     * Sets state to disconnected.
     *
     * @param disconnectCause The reason for the disconnection, as specified by
     *         {@link DisconnectCause}.
     */
    public final void setDisconnected(DisconnectCause disconnectCause) {
        checkImmutable();
        mDisconnectCause = disconnectCause;
        setState(STATE_DISCONNECTED);
        Log.d(this, "Disconnected with cause %s", disconnectCause);
        for (Listener l : mListeners) {
            l.onDisconnected(this, disconnectCause);
        }
    }

在这里做了两件事
1、把状态设置成STATE_DISCONNECTED。(步骤47)
2、通知绑定了监听器的人电话已挂断。(步骤48,49和50,最后通知了ConnectionServiceWrapper.java)

步骤50~53: ConnectionServiceWrapper.java的setDisconnected方法

        @Override
        public void setDisconnected(String callId, DisconnectCause disconnectCause) {
            logIncoming("setDisconnected %s %s", callId, disconnectCause);
            if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
                Log.d(this, "disconnect call %s", callId);
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = callId;
                args.arg2 = disconnectCause;
                mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
            }
        }

通过obtainMessage()创建了一个消息类型为MSG_SET_DISCONNECTED的Message,
在mHandler的handleMessage()方法里有相应的处理

步骤54: CallsManager.java的markCallAsDisconnected()方法

    /**
     * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
     * last live call, then also disconnect from the in-call controller.
     *
     * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}.
     */
    void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
        call.setDisconnectCause(disconnectCause);
        int prevState = call.getState();
        setCallState(call, CallState.DISCONNECTED);
        String activeSub = getActiveSubscription();
        String conversationSub = getConversationSub();
        String lchSub = IsAnySubInLch();

        PhoneAccount phAcc =
                 getPhoneAccountRegistrar().getPhoneAccount(call.getTargetPhoneAccount());
        if ((call.getTargetPhoneAccount() != null &&
                    call.getTargetPhoneAccount().getId().equals(activeSub)) &&
                    (phAcc != null) && (phAcc.isSet(PhoneAccount.LCH)) &&
                    (conversationSub != null) &&
                    (!conversationSub.equals(activeSub))) {
            Log.d(this,"Set active sub to conversation sub");
            setActiveSubscription(conversationSub);
        } else if ((conversationSub == null) && (lchSub != null) &&
                ((prevState == CallState.CONNECTING) || (prevState == CallState.PRE_DIAL_WAIT)) &&
                (call.getState() == CallState.DISCONNECTED)) {
            Log.d(this,"remove sub with call from LCH");
            updateLchStatus(lchSub);
            setActiveSubscription(lchSub);
            manageMSimInCallTones(false);
        }

        if ((call.getTargetPhoneAccount() != null) && (phAcc != null) &&
                (phAcc.isSet(PhoneAccount.LCH))) {
            Call activecall = getFirstCallWithStateUsingSubId(call.getTargetPhoneAccount().getId(),
                    CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
            Log.d(this,"activecall: " + activecall);
            if (activecall == null) {
                phAcc.unSetBit(PhoneAccount.LCH);
                manageMSimInCallTones(false);
            }
        }
    }

步骤55:把状态设置成了DISCONNECTED
步骤56~66:最后通知监听者Call的状态已改变,一直传递到UI界面。最后由CallCardFragment的setCallState方法更新界面,显示已挂断的界面。
步骤67: 底层上报Call的状态已改变的消息。
步骤68: 发出查询Call list状态列表的请求。
步骤69: 底层返回Call list,由CdmaCallTracker进行处理。

主动挂断电话的流程就这么多了。


二、按电源键物理键挂断通话


步骤1: 在PhoneWindowManager.java (frameworks\base\policy\src\com \android\internal\policy\impl)的interceptKeyBeforeQueueing()里有对物理键的相关事件处理。

         case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
                            event.getDownTime(), isImmersiveMode(mLastSystemUiFlags));
                    if (panic) {
                        mHandler.post(mRequestTransientNav);
                    }
                    if (interactive && !mPowerKeyTriggered
                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                        mPowerKeyTriggered = true;
                        mPowerKeyTime = event.getDownTime();
                        interceptScreenshotChord();
                        interceptScreenshotLog();

                        if (mButtonLightEnabled) {
                            try {
                                mLight.setButtonLightEnabled(false);
                            } catch(RemoteException e) {
                                Slog.e(TAG, "remote call for turn off button light failed.");
                            }
                        }
                    }

                    TelecomManager telecomManager = getTelecommService();
                    boolean hungUp = false;
                    if (telecomManager != null) {
                        if (telecomManager.isRinging()) {
                            // Pressing Power while there's a ringing incoming
                            // call should silence the ringer.
                            telecomManager.silenceRinger();
                        } else if ((mIncallPowerBehavior
                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                                && telecomManager.isInCall() && interactive) {
                            // Otherwise, if "Power button ends call" is enabled,
                            // the Power button will hang up any current active call.
                            hungUp = telecomManager.endCall();
                        }  else {
                            mContext.sendBroadcast(new Intent(POWER_KEYDOWN_MUTE));
                        }
                    }
                    interceptPowerKeyDown(event, interactive);
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);
                }
         break;
    }

如果正在通话中,电源键被点击,则说明用户想挂断电话(用户开了辅助功能的话),则会调用telecomManager.endCall()来挂断电话。

步骤2: TelecomManager.java的endCall()方法

    /**
     * Ends an ongoing call.
     * TODO: L-release - need to convert all invocations of ITelecomService#endCall to use this
     * method (clockwork & gearhead).
     * @hide
     */
    @SystemApi
    public boolean endCall() {
        try {
            if (isServiceConnected()) {
                return getTelecomService().endCall();
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling ITelecomService#endCall", e);
        }
        return false;
    }

在这里通过getTelecomService()方法获取到ITelecomService接口,远程调用TelecomService.java的endCall()方法

步骤3~5: TelecomService.java的endCall()方法

   /**
    * @see android.telecom.TelecomManager#endCall
    */
    @Override
    public boolean endCall() {
        enforceModifyPermission();
        return (boolean) sendRequest(MSG_END_CALL);
    }

   /**
     * Posts the specified command to be executed on the main thread, waits for the request to
     * complete, and returns the result.
     */
    private Object sendRequest(int command) {
        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
            MainThreadRequest request = new MainThreadRequest();
            mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
            return request.result;
        } else {
            MainThreadRequest request = sendRequestAsync(command, 0);

            // Wait for the request to complete
            synchronized (request) {
                while (request.result == null) {
                    try {
                        request.wait();
                    } catch (InterruptedException e) {
                        // Do nothing, go back and wait until the request is complete
                    }
                }
            }
            return request.result;
        }
    }

在这里通过obtainMessage()方法创建了一个消息类型为MSG_END_CALL的Message,然后传递给mMainThreadHandler.handleMessage()方法,让它处理这个Message。

步骤6~8: TelecomService.java的endCall()方法

private final class MainThreadHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.obj instanceof MainThreadRequest) {
                MainThreadRequest request = (MainThreadRequest) msg.obj;
                Object result = null;
                switch (msg.what) {
                ...
                case MSG_END_CALL:
                        result = endCallInternal();
                        break;
                ...
   }

   private boolean endCallInternal() {
        // Always operate on the foreground call if one exists, otherwise get the first call in
        // priority order by call-state.
        Call call = mCallsManager.getForegroundCall();
        if (call == null) {
            call = mCallsManager.getFirstCallWithState(
                    CallState.ACTIVE,
                    CallState.DIALING,
                    CallState.RINGING,
                    CallState.ON_HOLD);
        }

        if (call != null) {
            if (call.getState() == CallState.RINGING) {
                call.reject(false /* rejectWithMessage */, null);
            } else {
                call.disconnect();
            }
            return true;
        }

        return false;
    }

MSG_END_CALL消息是由endCallInternal()来处理,如果当前正在响铃的话就执行拒接电话的流程;否则的话,就调用call.disconnect()执行挂断电话的流程。这里的Call.java是在packages\services\telecomm\src\com\android\server\telecom目录下的。

步骤9:之后的流程跟点击挂断按钮方式的挂断流程步骤16~69相同,在这里就不重复了


三、远程挂断通话

远程挂断通话


RILJ收到Call状态变化通知RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,接着通知CdmaCallTracker,CdmaCallTracker则在pollCallsWhenSafe方法里调用RILJ的getCurrentCalls方法请求查询Call状态列表,底层处理完之后发出通知,CdmaCallTracker在handleMessage里进行响应,然后进入handlePollCalls方法里


1. CdmaCallTracker.java的handlePollCalls()方法

上面的流程跟接电话的流程都是有很多相似的,所以这里一笔带过,重点关注CdmaCallTracker的 handlePollCalls 方法

protected void
    handlePollCalls(AsyncResult ar){
    ...
    if (mDroppedDuringPoll.size() > 0) {
            mCi.getLastCallFailCause(
            obtainNoPollCompleteMessage(
            EVENT_GET_LAST_CALL_FAIL_CAUSE));
        }
    ...
}

2. RIL.java的getLastCallFailCause()方法

在这里会先调用 RILgetLastCallFailCause 方法向RIL查询 ,注意这里传进来了一个消息类型为EVENT_GET_LAST_CALL_FAIL_CAUSE的Message

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

对应的log是

08-04 13:22:45.842 D/RILJ    ( 3034): [5549]> LAST_CALL_FAIL_CAUSE

3. RIL.java的processSolicited()方法

RIL向底层发送了请求,底层处理完成之后,就会把处理的结果返回给RIL, RIL.javaprocessSolicited 方法里处理

private RILRequest processSolicited (Parcel p) {
        ...
        case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: 
        ret =  responseInts(p); break;
        ...
        //打印log日志
        if (RILJ_LOGD) riljLog(rr.serialString() + "< " + 
        requestToString(rr.mRequest)
                    + " " + retToString(rr.mRequest, ret));
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, null, tr);
        rr.mResult.sendToTarget();//发出handler消息通知
}

对应的log是

08-04 13:22:45.847 D/RILJ    ( 3034): [5549]< LAST_CALL_FAIL_CAUSE {16}

4. CdmaCallTracker.java的handleMessage()方法

rr.mResult.sendToTarget()发出handler消息通知后,会在 CdmaCallTrackerhandleMessage 方法中响应。

public void
    handleMessage (Message msg) {
        ...
        switch (msg.what) {
            case EVENT_GET_LAST_CALL_FAIL_CAUSE:
                int causeCode;
                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 {
                    causeCode = ((int[])ar.result)[0];
                }

                for (int i = 0, s =  mDroppedDuringPoll.size()
                        ; i < s ; i++
                ) {
                    CdmaConnection conn = mDroppedDuringPoll.get(i);

                    conn.onRemoteDisconnect(causeCode);
                }
                //更新phone状态
                updatePhoneState();

                mPhone.notifyPreciseCallStateChanged();
                //清除断开的通话连接
                mDroppedDuringPoll.clear();
            break;
   ...
}

conn.onRemoteDisconnect(causeCode); 通话断开的原因就是causeCode,conn是CdmaConnection类型的,下一步进入CdmaConnection.java


5. CdmaConnection.java的onRemoteDisconnect()方法

void onRemoteDisconnect(int causeCode) {          
    onDisconnect(disconnectCauseFromCode(causeCode));
}

/** Called when the radio indicates the connection has been disconnected */
boolean onDisconnect(DisconnectCause cause) {
        boolean changed = false;
        mCause = cause;
        if (!mDisconnected) {
            doDisconnect();
            if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
            mOwner.mPhone.notifyDisconnect(this);
            if (mParent != null) {
                changed = mParent.connectionDisconnected(this);
            }
        }
        releaseWakeLock();
        return changed;
}

看到这一行mPhone.notifyDisconnect(this); 因为我们这里研究的是CDMA,所以mPhone就是PhoneBase的其中一个子类CDMAPhone


6. CDMAPhone.java的notifyDisconnect()方法

void notifyDisconnect(Connection cn) {
        mDisconnectRegistrants.notifyResult(cn);
}

我们要找出mDisconnectRegistrants被调用的地方,我们先来到CallManager.java的registerForDisconnect方法里,继续找,我们要找的是“phone.registerForDisconnect”,在CallManager.java找到下面这行:

phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);

可以知道,是CallManager.java监听了EVENT_DISCONNECT事件,所以会在它的handleMessage方法里响应这个消息


7. CallManager.java的handleMessage方法

public void handleMessage(Message msg) {
 switch (msg.what) {
      case EVENT_DISCONNECT:
      if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
mDisconnectRegistrants.notifyRegistrants((
                      AsyncResult) msg.obj);
       break;
       ...
}

又是mDisconnectRegistrants,还是要找它的被调用的地方,不过这次要找的是“callManager.registerForDisconnect”,在CallStateMonitor.java里找到这行:

callManager.registerForDisconnect(this, PHONE_DISCONNECT, null);

这里有个要注意的地方,CallStateMonitor并不是Handler的子类,所以它没有handleMessage方法,那么它监听了PHONE_DISCONNECT事件,在哪里响应并且处理它呢,我们要找PHONE_DISCONNECT被调用的地方,在CallNotifier.java的handleMessage方法里找到了

public void handleMessage(Message msg) {
 switch (msg.what) {
      ...
      case CallStateMonitor.PHONE_DISCONNECT:
                if (DBG) log("DISCONNECT");
                onDisconnect((AsyncResult) msg.obj);
                break;
      ...
}

所以CallNotifier会响应PHONE_DISCONNECT事件。


最后,贴出上面分析的远程挂断通话流程的log

08-04 13:22:45.816 D/RILJ    ( 3034):[UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-04 13:22:45.816 D/RILJ    ( 3034): [5548]> GET_CURRENT_CALLS
08-04 13:22:45.832 D/RILJ    ( 3034): [5548]< GET_CURRENT_CALLS  
08-04 13:22:45.842 D/RILJ    ( 3034): [5549]> LAST_CALL_FAIL_CAUSE
08-04 13:22:45.845 D/CdmaCallTracker( 3034): 
[CdmaCallTracker] update phone state, old=OFFHOOK new=OFFHOOK
08-04 13:22:45.847 D/RILJ    ( 3034): [5549]< LAST_CALL_FAIL_CAUSE {16}
08-04 13:22:45.857 D/CdmaCallTracker( 3034): 
[CdmaCallTracker] update phone state, old=OFFHOOK new=IDLE
08-04 13:22:46.361 D/CdmaCallTracker( 3034): 
[CdmaCallTracker] update phone state, old=IDLE new=IDLE
08-04 13:22:46.469 D/CallNotifier( 3034): DISCONNECT
08-04 13:22:46.469 D/CallNotifier( 3034): onDisconnect()...  CallManager state: IDLE
08-04 13:22:46.470 D/CallNotifier( 3034): 
onDisconnect: cause = NORMAL, incoming = true, date = 1438665738497
08-04 13:22:46.502 D/CallNotifier( 3034): 
stopSignalInfoTone: Stopping SignalInfo tone player
08-04 13:22:46.504 D/CallNotifier( 3034): stopRing()... (onDisconnect)
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值