Android6.0的phone应用源码分析(8)——来电(MT)

1.1   TelePhony Framework

当有新来电时,最先得到消息的肯定是Modem,接着Modem response到RILD,RILD通过“rild”socket  accpet得到的clientsocket将消息上传到RILJ。

而在之前分析MO的RILJ时,已经知道存在着两种消息Solicited消息和UnSolicited消息。UnSolicited消息可以基本认定为是底层主动上报的消息,因此对于Call来电消息,根据之前的分析会交给processUnsolicited()处理。请查看processUnsolicited()中关于消息RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED的处理(已标黄)。可以看到实际上的处理就是向此RIL消息的注册者们mCallStateRegistrants发出了通知。其处理过程大致如下图所示。


应该注意到的是,一个Phone除了基本的通话功能之外其实还存在着其他功能,比如移动网络,这些都需要依赖于手机卡的支持。因此,将phone的不同功能划分开来是很有必要的。因此就有了CallTracker负责通话功能,ServiceStateTracker提供状态服务,DcTracker负责网络数据通信服务的划分,当然他们三者之间也是存在联系的,暂不详述。

图中三类Tracker都继承了Handler类。对于非URC消息,Tracker在下发命令的时候会携带mResult参数以指明应答的Handler,RILJ可以根据这个Handler将消息传递的上层。而对于URC消息(其实还包含部分非URC消息),由于是底层主动上报的消息,我们很容易就能想到会使用观察者模式:RILJ维护多个RegistrantList,上层向RIL注册将自己注册到某个自己感兴趣的事件对应的RegistrantList,当有感兴趣的事件发生时,相应的上层结构得到RILJ的通知。

RILJ维护了很多这样的RegistrantList,用以区分不同事件的感兴趣者。对于来电,CdmaCallTracker已经在构造函数中向RILJ的mCallStateRegistrants注册。因此,处理此消息的为CdmaCallTracker。找到其handlerMessage()方法。需要注意的是来电首先触发的消息是RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,RILJ将这个消息通知到其注册者CdmaCallTracker。此时Message携带的Event消息为EVENT_CALL_STATE_CHANGE,其处理过程如下所示。

public void handleMessage (Message msg) {

        AsyncResult ar;

 

        if (!mPhone.mIsTheCurrentActivePhone) {

            Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone");

            return;

        }

        switch (msg.what) {

            case EVENT_POLL_CALLS_RESULT:{ //MT第二次

                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);//MT_2时候走这里

                }

            }

            break;

 

            case EVENT_OPERATION_COMPLETE:

                operationComplete(); //之前的MO时进入过这里

            break;

 

            case EVENT_SWITCH_RESULT:

                 // In GSM call operationComplete() here which gets the

                 // current call list. But in CDMA there is no list so

                 // there is nothing to do.

            break;

 

            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;

                }

 

                for (int i = 0, s =  mDroppedDuringPoll.size()

                        ; i < s ; i++

                ) {

                    CdmaConnection conn = mDroppedDuringPoll.get(i);

 

                    conn.onRemoteDisconnect(causeCode, vendorCause);

                }

 

                updatePhoneState();

 

                mPhone.notifyPreciseCallStateChanged();

                mDroppedDuringPoll.clear();

            break;

 

            case EVENT_REPOLL_AFTER_DELAY:

            case EVENT_CALL_STATE_CHANGE: //MT第一次

                //调用父类CallTracker查询Call List方法

                pollCallsWhenSafe(); //MO时也进入这里,此时MT_1也进入

            break;

 

            case EVENT_RADIO_AVAILABLE:

                handleRadioAvailable();

            break;

 

            case EVENT_RADIO_NOT_AVAILABLE:

                handleRadioNotAvailable();

            break;

 

            case EVENT_EXIT_ECM_RESPONSE_CDMA:

                // no matter the result, we still do the same here

                if (mPendingCallInEcm) {

                    mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage());

                    mPendingCallInEcm = false;

                }

                mPhone.unsetOnEcbModeExitResponse(this);

            break;

 

            case EVENT_CALL_WAITING_INFO_CDMA:

               ar = (AsyncResult)msg.obj;

               if (ar.exception == null) {

                   handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);

                   Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");

               }

            break;

 

            case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:

                ar = (AsyncResult)msg.obj;

                if (ar.exception == null) {

                    // Assume 3 way call is connected

                    mPendingMO.onConnectedInOrOut();

                    mPendingMO = null;

                }

            break;

 

            case EVENT_THREE_WAY_DIAL_BLANK_FLASH:

                ar = (AsyncResult) msg.obj;

                if (ar.exception == null) {

                    postDelayed(

                            new Runnable() {

                                public void run() {

                                    if (mPendingMO != null) {

                                        mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),

                                                obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));

                                    }

                                }

                            }, m3WayCallFlashDelay);

                } else {

                    mPendingMO = null;

                    Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call");

                }

            break;

 

            default:{

               throw new RuntimeException("unexpected event not handled");

            }

        }

    }

根据之前的分析,MT第一次case将进入pollCallsWhenSafe(),其实现在其父类CallTracker中找到:

    protected void pollCallsWhenSafe() {

        mNeedsPoll = true;

 

        if (checkNoOperationsPending()) {

            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);

            mCi.getCurrentCalls(mLastRelevantPoll);

        }

    }

仔细对比就可以发现,这些处理其实和之前MO分析时候的处理时是一样的。它会进入RILJ的getCurrentCalls()。mCi就是RILJ,这里需要注意的是之前RILJ上报的消息之所以能够case到EVENT_CALL_STATE_CHANGE是应为在cdmaCallTracker向RILJ注册的参数已经指明了是EVENT_CALL_STATE_CHANGE。而这里不一样,这里是非URC消息,所匹配的EVENT消息是在下发CMD的时候指明的(上面金底红字部分)。

 public void getCurrentCalls (Message result) {

        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

        send(rr);

    }

此时的RILJ又向下层下发了SolicitedRequest,下层处理后再次进入ProcessSolicited(),第二次case调用rr.mResult.sendToTarget()向CallTracker发出消息。handleMessage()调用handlePollCalls((AsyncResult)msg.obj)根据RIL发出的Call List对象,判断Call的状态并发出通知,这里发出的是新来电通知Phone.notifyNewRingingConnection(),handlePollCalls代码之前MO已经贴过了,这里只截取相关代码如下。

protected void handlePollCalls(AsyncResult ar) {

        ……

        if (newRinging != null) {

            mPhone.notifyNewRingingConnection(newRinging);

        }

……

}

 

--------àCDMAPhone.java

public void notifyNewRingingConnection(Connection c) {

        super.notifyNewRingingConnectionP(c);

}

回调了基类PhoneBase的notifyNewRingingConnectionP(c)方法。此时来电消息进入了Framework层。

    public void notifyNewRingingConnectionP(Connection cn) {

        if (!mIsVoiceCapable)

            return;

        AsyncResult ar = new AsyncResult(null, cn, null);

        mNewRingingConnectionRegistrants.notifyRegistrants(ar);

    }

可以看出是向监听mNewRingConnectionRegistrant的观察者发送消息,这个消息包含了一个connection。此消息将在TelephonyService模块中处理。

1.2   Telephony Service

首先要分析的是这个消息的观察者是谁。在SourceInsight中查找,发现有多个地方注册了这个事件,包括CallManager,PstnIncomingCallNotifier。CallManager虽然注册了该消息但是其向上二次上报的观察者为空(CallStateMonitor中原先有注册,但在Android6.0中已经被注释掉了,并且Android6.0已经注明可以删除与这些注释相关的代码),因此查看PstnIncomingCallNotifier的相关方法:

private void registerForNotifications() {

        Phone newPhone = mPhoneProxy.getActivePhone();

        if (newPhone != mPhoneBase) {

            unregisterForNotifications();

 

            if (newPhone != null) {

                Log.i(this, "Registering: %s", newPhone);

                mPhoneBase = newPhone;

                mPhoneBase.registerForNewRingingConnection(

                        mHandler, EVENT_NEW_RINGING_CONNECTION, null);

                mPhoneBase.registerForCallWaiting(

                        mHandler, EVENT_CDMA_CALL_WAITING, null);

                mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION,

                        null);

            }

        }

    }

从以上代码可以看出新来电的观察者为PstnIncomingCallNotifier。找到mHandler的相关方法

private final Handler mHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch(msg.what) {

                case EVENT_NEW_RINGING_CONNECTION:

                    handleNewRingingConnection((AsyncResult) msg.obj);

                    break;

                case EVENT_CDMA_CALL_WAITING:

                    handleCdmaCallWaiting((AsyncResult) msg.obj);

                    break;

                case EVENT_UNKNOWN_CONNECTION:

                    handleNewUnknownConnection((AsyncResult) msg.obj);

                    break;

                default:

                    break;

            }

        }

    };

进入handleNewRingingConnection()。

    private void handleNewRingingConnection(AsyncResult asyncResult) {

        Log.d(this, "handleNewRingingConnection");

        Connection connection = (Connection) asyncResult.result;//解消息

        if (connection != null) {

            Call call = connection.getCall();//获得对应的Call

 

            // intent发送给 Telecom.

            if (call != null && call.getState().isRinging()) {

                sendIncomingCallIntent(connection);

            }

        }

    }

具体的发送过程

    private void sendIncomingCallIntent(Connection connection) {

        Bundle extras = null;

        if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&

                !TextUtils.isEmpty(connection.getAddress())) {

            extras = new Bundle();

            Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);

            extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);

        }

        TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(

                PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy), extras);

    }

进入TelecomManager

public static TelecomManager from(Context context) {

        return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);

    }

/**

* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it has an incoming call. The specified {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request additional information about the call (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI. @param phoneAccount A {@link PhoneAccountHandle} registered with{@link #registerPhoneAccount}.@param extras A bundle that will be passed through to{@link ConnectionService#onCreateIncomingConnection}.

*/

    public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {

        try {

            if (isServiceConnected()) {

                getTelecomService().addNewIncomingCall(

                        phoneAccount, extras == null ? new Bundle() : extras);

            }

        } catch (RemoteException e) {

            Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);

        }

    }

因此进入TelecomServiceImpl

public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {

            synchronized (mLock) {

                Log.i(this, "Adding new incoming call with phoneAccountHandle %s",

                        phoneAccountHandle);

                if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {

                    // TODO(sail): Add unit tests for adding incoming calls from a SIM call manager.

                    if (isCallerSimCallManager() && TelephonyUtil.isPstnComponentName(

                            phoneAccountHandle.getComponentName())) {

                        Log.v(this, "Allowing call manager to add incoming call with PSTN handle");

                    } else {

                        mAppOpsManager.checkPackage(

                                Binder.getCallingUid(),

                               phoneAccountHandle.getComponentName().getPackageName());

                        // Make sure it doesn't cross the UserHandle boundary

                        enforceUserHandleMatchesCaller(phoneAccountHandle);

                    }

 

                    long token = Binder.clearCallingIdentity();

                    try {

                        Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);

                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,

                            phoneAccountHandle);

                        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);

                        if (extras != null) {

                            intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);

                        }

                        CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent);

                    } finally {

                        Binder.restoreCallingIdentity(token);

                    }

                } else {

                    Log.w(this,

                            "Null phoneAccountHandle. Ignoring request to add new incoming call");

                }

            }

        }

熟悉的CallIntentProcessor,之前在的MO的时候启用过它的processOutgoingCallIntent()方法。此时来电调用的是processIncomingCallIntent(),如下

    static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {

        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(

                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);

 

        if (phoneAccountHandle == null) {

            Log.w(CallIntentProcessor.class,

                    "Rejecting incoming call due to null phone account");

            return;

        }

        if (phoneAccountHandle.getComponentName() == null) {

            Log.w(CallIntentProcessor.class,

                    "Rejecting incoming call due to null component name");

            return;

        }

 

        Bundle clientExtras = null;

        if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {

            clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);

        }

        if (clientExtras == null) {

            clientExtras = new Bundle();

        }

 

        Log.d(CallIntentProcessor.class,

                "Processing incoming call from connection service [%s]",

                phoneAccountHandle.getComponentName());

        callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);

    }

进入CallsManager的processIncomingCallIntent()方法

void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {

        Log.d(this, "processIncomingCallIntent");

        Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);

        if (handle == null) {

            // Required for backwards compatibility

            handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);

        }

        Call call = new Call(

                mContext,

                this,

                mLock,

                mConnectionServiceRepository,

                mContactsAsyncHelper,

                mCallerInfoAsyncQueryFactory,

                handle,

                null /* gatewayInfo */,

                null /* connectionManagerPhoneAccount */,

                phoneAccountHandle,

                true /* isIncoming */,

                false /* isConference */);

 

        call.setIntentExtras(extras);

        // TODO: Move this to be a part of addCall()

        call.addListener(this);

        call.startCreateConnection(mPhoneAccountRegistrar);

    }

再次回到熟悉的地方,在之前的MO分析是已经分析过了。主要是新建了一个连接执行器。并调用其process方法建立connection

void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {

        Preconditions.checkState(mCreateConnectionProcessor == null);

        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,

                phoneAccountRegistrar, mContext);

        mCreateConnectionProcessor.process();

    }

之前在MO中已经贴了代码,这里不厌其烦将后面的代码再贴几段一次。(与MO有区别的地方已经蓝底标出来了)。

    void process() {

        Log.v(this, "process");

        clearTimeout();

        mAttemptRecords = new ArrayList<>();

        if (mCall.getTargetPhoneAccount() != null) {

            mAttemptRecords.add(new CallAttemptRecord(

                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));

        }

        adjustAttemptsForConnectionManager();

        adjustAttemptsForEmergency();

        mAttemptRecordIterator = mAttemptRecords.iterator();

        attemptNextPhoneAccount();

    }

一样进入attemptNextPhoneAccount:

private void attemptNextPhoneAccount() {

        Log.v(this, "attemptNextPhoneAccount");

        CallAttemptRecord attempt = null;

        if (mAttemptRecordIterator.hasNext()) {

            attempt = mAttemptRecordIterator.next();

 

            if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(

                    attempt.connectionManagerPhoneAccount)) {

                Log.w(this,

                        "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for "

                                + "attempt: %s", attempt);

                attemptNextPhoneAccount();

                return;

            }

 

            // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it

            // also requires the BIND_TELECOM_CONNECTION_SERVICE permission.

            if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&

                    !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(

                            attempt.targetPhoneAccount)) {

                Log.w(this,

                        "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for "

                                + "attempt: %s", attempt);

                attemptNextPhoneAccount();

                return;

            }

        }

 

        if (mResponse != null && attempt != null) {

            Log.i(this, "Trying attempt %s", attempt);

            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;

            ConnectionServiceWrapper service =

                    mRepository.getService(

                            phoneAccount.getComponentName(),

                            phoneAccount.getUserHandle());

            if (service == null) {

                Log.i(this, "Found no connection service for attempt %s", attempt);

                attemptNextPhoneAccount();

            } else {

                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);

                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);

                mCall.setConnectionService(service);

                setTimeoutIfNeeded(service, attempt);

 

                service.createConnection(mCall, new Response(service));

            }

        } else {

            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");

            if (mResponse != null) {

                clearTimeout();

                mResponse.handleCreateConnectionFailure(mLastErrorDisconnectCause != null ?

                        mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR));

                mResponse = null;

                mCall.clearConnectionService();

            }

        }

    }

一样进入到ConnectionServiceWrapper的createConnection():

void createConnection(final Call call, final CreateConnectionResponse response) {

        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());

        BindCallback callback = new BindCallback() {

            @Override

            public void onSuccess() {

                String callId = mCallIdMapper.getCallId(call);

                mPendingResponses.put(callId, response);

 

                GatewayInfo gatewayInfo = call.getGatewayInfo();

                Bundle extras = call.getIntentExtras();

                if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&

                        gatewayInfo.getOriginalAddress() != null) {

                    extras = (Bundle) extras.clone();

                    extras.putString(

                            TelecomManager.GATEWAY_PROVIDER_PACKAGE,

                            gatewayInfo.getGatewayProviderPackageName());

                    extras.putParcelable(

                            TelecomManager.GATEWAY_ORIGINAL_ADDRESS,

                            gatewayInfo.getOriginalAddress());

                }

 

                Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));

                try {

                    mServiceInterface.createConnection(

                            call.getConnectionManagerPhoneAccount(),

                            callId,

                            new ConnectionRequest(

                                    call.getTargetPhoneAccount(),

                                    call.getHandle(),

                                    extras,

                                    call.getVideoState()),

                            call.isIncoming(),

                            call.isUnknown());

                } catch (RemoteException e) {

                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());

                    mPendingResponses.remove(callId).handleCreateConnectionFailure(

                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));

                }

            }

 

            @Override

            public void onFailure() {

                Log.e(this, new Exception(), "Failure to call %s", getComponentName());

                response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));

            }

        };

 

        mBinder.bind(callback, call);

    }

进入到ConnectionService.createConnection

private void createConnection(

            final PhoneAccountHandle callManagerAccount,

            final String callId,

            final ConnectionRequest request,

            boolean isIncoming,

            boolean isUnknown) {

        Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +

                "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,

                isUnknown);

 

        Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)

                : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)

                : onCreateOutgoingConnection(callManagerAccount, request);

        Log.d(this, "createConnection, connection: %s", connection);

        if (connection == null) {

            connection = Connection.createFailedConnection(

                    new DisconnectCause(DisconnectCause.ERROR));

        }

 

        if (connection.getState() != Connection.STATE_DISCONNECTED) {

            addConnection(callId, connection);

        }

 

        Uri address = connection.getAddress();

        String number = address == null ? "null" : address.getSchemeSpecificPart();

        Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",

                Connection.toLogSafePhoneNumber(number),

                Connection.stateToString(connection.getState()),

                Connection.capabilitiesToString(connection.getConnectionCapabilities()));

 

        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);

        mAdapter.handleCreateConnectionComplete(

                callId,

                request,

                new ParcelableConnection(

                        request.getAccountHandle(),

                        connection.getState(),

                        connection.getConnectionCapabilities(),

                        connection.getAddress(),

                        connection.getAddressPresentation(),

                        connection.getCallerDisplayName(),

                        connection.getCallerDisplayNamePresentation(),

                        connection.getVideoProvider() == null ?

                                null : connection.getVideoProvider().getInterface(),

                        connection.getVideoState(),

                        connection.isRingbackRequested(),

                        connection.getAudioModeIsVoip(),

                        connection.getConnectTimeMillis(),

                        connection.getStatusHints(),

                        connection.getDisconnectCause(),

                        createIdList(connection.getConferenceables()),

                        connection.getExtras()));

    }

 

    void handleCreateConnectionComplete(

            String id,

            ConnectionRequest request,

            ParcelableConnection connection) {

        for (IConnectionServiceAdapter adapter : mAdapters) {

            try {

                adapter.handleCreateConnectionComplete(id, request, connection);

            } catch (RemoteException e) {

            }

        }

    }

 

public void handleCreateConnectionComplete(

                String callId,

                ConnectionRequest request,

                ParcelableConnection connection) {

            long token = Binder.clearCallingIdentity();

            try {

                synchronized (mLock) {

                    logIncoming("handleCreateConnectionComplete %s", callId);

                    if (mCallIdMapper.isValidCallId(callId)) {

                        ConnectionServiceWrapper.this

                                .handleCreateConnectionComplete(callId, request, connection);

                    }

                }

            } finally {

                Binder.restoreCallingIdentity(token);

            }

        }

这里回调了外围类的同名函数。

    private void handleCreateConnectionComplete(

            String callId,

            ConnectionRequest request,

            ParcelableConnection connection) {

        // TODO: Note we are not using parameter "request", which is a side effect of our tacit

        // assumption that we have at most one outgoing connection attempt per ConnectionService.

        // This may not continue to be the case.

        if (connection.getState() == Connection.STATE_DISCONNECTED) {

            // A connection that begins in the DISCONNECTED state is an indication of

            // failure to connect; we handle all failures uniformly

            removeCall(callId, connection.getDisconnectCause());

        } else {

            // 创建connection成功

            if (mPendingResponses.containsKey(callId)) {

                mPendingResponses.remove(callId)

                        .handleCreateConnectionSuccess(mCallIdMapper, connection);

            }

        }

    }

mPendingResponse存储的是Call.因此Call的方法被调用。

    @Override

    public void handleCreateConnectionSuccess(

            CallIdMapper idMapper,

            ParcelableConnection connection) {

        Log.v(this, "handleCreateConnectionSuccessful %s", connection);

        setTargetPhoneAccount(connection.getPhoneAccount());

        setHandle(connection.getHandle(), connection.getHandlePresentation());

        setCallerDisplayName(

                connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());

        setConnectionCapabilities(connection.getConnectionCapabilities());

        setVideoProvider(connection.getVideoProvider());

        setVideoState(connection.getVideoState());

        setRingbackRequested(connection.isRingbackRequested());

        setIsVoipAudioMode(connection.getIsVoipAudioMode());

        setStatusHints(connection.getStatusHints());

        setExtras(connection.getExtras());

 

        mConferenceableCalls.clear();

        for (String id : connection.getConferenceableConnectionIds()) {

            mConferenceableCalls.add(idMapper.getCall(id));

        }

 

        if (mIsUnknown) {

            for (Listener l : mListeners) {

                l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));

            }

        } else if (mIsIncoming) {

            // We do not handle incoming calls immediately when they are verified by the connection

            // service. We allow the caller-info-query code to execute first so that we can read the

            // direct-to-voicemail property before deciding if we want to show the incoming call to

            // the user or if we want to reject the call.

            mDirectToVoicemailQueryPending = true;

 

            // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before

            // showing the user the incoming call screen.

            mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(

                    mContext.getContentResolver()));

        } else {

            for (Listener l : mListeners) {

                l.onSuccessfulOutgoingCall(this,

                        getStateFromConnectionState(connection.getState()));

            }

        }

    }

都是谁注册进了mListeners?仔细查看代码发现CallsManager将自己注册进了这个mListeners,因此程序又回到了CallsManager。在onSuccessfulIncomingCall()方法中把Call的状态改成RINGING,并把此Call添加到Call集合里。并触发CallsManagerListener的OnCallAdded()开启UI界面。

@Override

    public void onSuccessfulIncomingCall(Call incomingCall) {

        Log.d(this, "onSuccessfulIncomingCall");

        setCallState(incomingCall, CallState.RINGING, "successful incoming call");

 

        if (hasMaximumRingingCalls()) {

            incomingCall.reject(false, null);

            // since the call was not added to the list of calls, we have to call the missed

            // call notifier and the call logger manually.

            mMissedCallNotifier.showMissedCallNotification(incomingCall);

            mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);

        } else {

            addCall(incomingCall);

        }

    }

进入addCall

private void addCall(Call call) {

        Trace.beginSection("addCall");

        Log.v(this, "addCall(%s)", call);

        call.addListener(this);

        mCalls.add(call);

 

        // TODO: Update mForegroundCall prior to invoking

        // onCallAdded for calls which immediately take the foreground (like the first call).

        for (CallsManagerListener listener : mListeners) {

            if (Log.SYSTRACE_DEBUG) {

                Trace.beginSection(listener.getClass().toString() + " addCall");

            }

            listener.onCallAdded(call);

            if (Log.SYSTRACE_DEBUG) {

                Trace.endSection();

            }

        }

        updateCallsManagerState();

        Trace.endSection();

    }

 

这里作一下小结,来电的大致过程如下图所示


1.3        UI界面的开启

待进一步分析研究

1.4        接听电话

此时假设UI界面已经呈现,并且响铃,按下接听键,AnswerFragment的onAnswer()被触发:

    public void onAnswer(int videoState, Context context) {

        Log.d(this, "onAnswer videoState=" + videoState + " context=" + context);

        getPresenter().onAnswer(videoState, context);

    }

进入AnswerPresenter.java的onAnswer():

public void onAnswer(int videoState, Context context) {

        if (mCallId == null) {

            return;

        }

 

        if (mCall.getSessionModificationState()

                == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {

            Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);

            InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);

        } else {

            Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);

            TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);

        }

    }

进入TelecomAdapter的answerCall():

    void answerCall(String callId, int videoState) {

        android.telecom.Call call = getTelecommCallById(callId); //根据ID得到Call

        if (call != null) {

            call.answer(videoState);

        } else {

            Log.e(this, "error answerCall, call not in call list: " + callId);

        }

    }

进入Call.answer()

    public void answer(int videoState) {

        mInCallAdapter.answerCall(mTelecomCallId, videoState);

    }

进入InCallAdapter.answer()

public void answerCall(String callId, int videoState) {

        try {

            mAdapter.answerCall(callId, videoState);//mAdapter是个IInCallAdapter

        } catch (RemoteException e) {

        }

}

//真正的InCallAdapter

 public void answerCall(String callId, int videoState) {

        long token = Binder.clearCallingIdentity();

        try {

            synchronized (mLock) {

                Log.d(this, "answerCall(%s,%d)", callId, videoState);

                if (mCallIdMapper.isValidCallId(callId)) {

                    Call call = mCallIdMapper.getCall(callId);//根据ID得到Call

                    if (call != null) {

                        mCallsManager.answerCall(call, videoState);//回到CallsManager

                    } else {

                        Log.w(this, "answerCall, unknown call id: %s", callId);

                    }

                }

            }

        } finally {

            Binder.restoreCallingIdentity(token);

        }

    }

又进入了CallsManager.java

    /**

     * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call

     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by

     * the user opting to answer said call.

     *

     * @param call The call to answer.

     * @param videoState The video state in which to answer the call.

     */

    void answerCall(Call call, int videoState) {

        if (!mCalls.contains(call)) {

            Log.i(this, "Request to answer a non-existent call %s", call);

        } else {

            // If the foreground call is not the ringing call and it is currently isActive() or

            // STATE_DIALING, put it on hold before answering the call.

            if (mForegroundCall != null && mForegroundCall != call &&

                    (mForegroundCall.isActive() ||

                     mForegroundCall.getState() == CallState.DIALING)) {

                if (0 == (mForegroundCall.getConnectionCapabilities()

                        & Connection.CAPABILITY_HOLD)) {

                    // This call does not support hold.  If it is from a different connection

                    // service, then disconnect it, otherwise allow the connection service to

                    // figure out the right states.

                    if (mForegroundCall.getConnectionService() != call.getConnectionService()) {

                        mForegroundCall.disconnect();

                    }

                } else {

                    Call heldCall = getHeldCall();

                    if (heldCall != null) {

                        Log.v(this, "Disconnecting held call %s before holding active call.",

                                heldCall);

                        heldCall.disconnect();

                    }

 

                    Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",

                            mForegroundCall, call);

                    mForegroundCall.hold();

                }

                // TODO: Wait until we get confirmation of the active call being

                // on-hold before answering the new call.

                // TODO: Import logic from CallManager.acceptCall()

            }

 

            for (CallsManagerListener listener : mListeners) {

                listener.onIncomingCallAnswered(call);

            }

 

            // We do not update the UI until we get confirmation of the answer() through

            // {@link #markCallAsActive}.

            call.answer(videoState); //进入Call(另一个,是TelecomService模块的)

            if (VideoProfile.isVideo(videoState) &&

                !mWiredHeadsetManager.isPluggedIn() &&

                !mCallAudioManager.isBluetoothDeviceAvailable() &&

                isSpeakerEnabledForVideoCalls()) {

                call.setStartWithSpeakerphoneOn(true);

            }

        }

    }

进入Call.answer:

    void answer(int videoState) {

        Preconditions.checkNotNull(mConnectionService);

 

        // Check to verify that the call is still in the ringing state. A call can change states

        // between the time the user hits 'answer' and Telecom receives the command.

        if (isRinging("answer")) {

            // At this point, we are asking the connection service to answer but we don't assume

            // that it will work. Instead, we wait until confirmation from the connectino service

            // that the call is in a non-STATE_RINGING state before changing the UI. See

            // {@link ConnectionServiceAdapter#setActive} and other set* methods.

            //这是个ConnectionServiceWrapper实例

            mConnectionService.answer(this, videoState);

            Log.event(this, Log.Events.REQUEST_ACCEPT);

        }

    }

进入mConnectionService.answer()

/** @see IConnectionService#answer(String) */

    void answer(Call call, int videoState) {

        final String callId = mCallIdMapper.getCallId(call);//获取CallID

        if (callId != null && isServiceValid("answer")) {

            try {

                logOutgoing("answer %s %d", callId, videoState);

                if (VideoProfile.isAudioOnly(videoState)) {//如果仅仅是音频通话

                    mServiceInterface.answer(callId);//调用Service同名方法

                } else {//视频通话

                   //调用Service同名方法

                    mServiceInterface.answerVideo(callId, videoState);

                }

            } catch (RemoteException e) {

            }

        }

    }

这里的东西一样很熟悉,根据之前的MO分析经验,最终调用的会是ConnectionService的answer()方法或answerVideo()方法。从下面可以看出他们唯一的区别就是参数。

    private void answerVideo(String callId, int videoState) {

        Log.d(this, "answerVideo %s", callId);

        findConnectionForAction(callId, "answer").onAnswer(videoState);

    }

 

    private void answer(String callId) {

        Log.d(this, "answer %s", callId);

        findConnectionForAction(callId, "answer").onAnswer();

    }

下面的过程就不详细描述了,最终肯定会调用RILJ的AccptCall方法,接收电话。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值