(学习笔记)android 5.0 系统去电流程状态判断(下)

Posted 王_健

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(学习笔记)android 5.0 系统去电流程状态判断(下)相关的知识,希望对你有一定的参考价值。

需求:

判断系统拨号的每个状态,根据状态不同弹窗提示。

需求分析:

1、无卡开机要求提示用户:Emergency calls only. Please enter Emergency Number;
2、无网络覆盖情况,当户用拨打所有号码,界面上立即给出提示“No network coverage!";
3、飞行模式打开的情况下,用户拨打紧急号码时,弹出提示,提示用户要先关闭飞行模式;
4、当两张卡都处于有限服务状态的时候 拨打号码, 手机提示"Emergency call only, Please enter an Emergency number!"

目的:

通过需求了解去电的基本流程。

附上本次要分析的流程图,正常情况下就不分析了,为了满足需求,主要分析的是去电情况下的异常状态的处理机制。

上一篇笔记分析到了这个流程图的第一步,在这个方法中Telecom模块绑定了InCallUI这个进程,可以通过mInCallServices对通话界面的状态进行各种控制。这篇笔记接着这个方法开始分析。

第1步:

static void processOutgoingCallIntent(Context context, Intent intent) 

        ...
        //首次绑定InCallUI进程。
        CallsManager.getInstance().getInCallController().bind();
        /// @

       ...
        //返回一个CALL对象,同时,startOutgoingCall()首次对InCallUI进行控制
        Call call = getCallsManager().startOutgoingCall(handle, phoneAccountHandle, clientExtras);
       ...

            NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                    context, getCallsManager(), call, intent, isDefaultDialer);
        //进入拨号流程
            final int result = broadcaster.processIntent();
            final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

            if (!success && call != null) 
                disconnectCallAndShowErrorDialog(context, call, result);
            
        
    

第2、3步:

int processIntent() 

       ...

        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);

    //仅对系统拨号器情况下进行判断;
    //紧急号码情况下,将Aciton 重写为 action = Intent.ACTION_CALL_EMERGENCY;
    //非紧急号码情况下,将Aciton 重写为 action = Intent.ACTION_CALL;
    //此时,第三方软件的Aciton一直是Intent.ACTION_CALL;
        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
        action = intent.getAction();
        // True for certain types of numbers that are not intended to be intercepted or modified
        // by third parties (e.g. emergency numbers).
        boolean callImmediately = false;

    //如果是紧急号码,系统拨号Action不会是 Intent.ACTION_CALL,可以判定第三方拨打的紧急号码
        if (Intent.ACTION_CALL.equals(action)) 
            if (isPotentialEmergencyNumber) 
                if (!mIsDefaultOrSystemPhoneApp) 
                    Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
                            + "unless caller is system or default dialer.", number, intent);
                    launchSystemDialer(intent.getData());
                    return DisconnectCause.OUTGOING_CANCELED;
                 else 
                    callImmediately = true;
                
            
         else if (Intent.ACTION_CALL_EMERGENCY.equals(action))  //系统拨打的紧急号码
            ...
            callImmediately = true;
         else 
            Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
            return DisconnectCause.INVALID_NUMBER;
        

        if (callImmediately) 
            Log.i(this, "Placing call immediately instead of waiting for "
                    + " OutgoingCallBroadcastReceiver: %s", intent);
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.VideoState.AUDIO_ONLY);
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);

            // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
            // so that third parties can still inspect (but not intercept) the outgoing call. When
            // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
            // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
        
    //紧急号码情况在这个方法下会被屏蔽,上面已经处理过
        broadcastIntent(intent, number, !callImmediately);
        return DisconnectCause.NOT_DISCONNECTED;
    

这一步对号码以及Action做了区分,根据具体情况重写了Aciton ;

  1. 系统拨打紧急号码会立即进入拨号流程;
  2. 第三方拨打紧急号码会立即跳转至系统拨号盘;
  3. 其他情况发送广播执行拨号流程。

第4步:

 void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
            int videoState) 
       ...

        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
                call.getHandle());
        Log.i(this, "placeOutgoingCall isEmergencyCall = " + isEmergencyCall);
       ...
    //如果是无卡状态,在这里就不会继续往下走了,上一篇判断的无卡状态,从log上看,走到这里就停止了。
        if (call.getTargetPhoneAccount() != null || isEmergencyCall) 
            // If the account has been set, proceed to place the outgoing call.
            // Otherwise the connection will be initiated when the account is set by the user.
            /// M: call control start. @
            // If already have an active call, need to hold active call first.
            Call activeCall = getFirstCallWithState(CallState.ACTIVE);
            if (activeCall == null 
                    || isPotentialMMICode(call.getHandle())
                    || isPotentialInCallMMICode(call.getHandle())) 
                Log.i(this, "Active call is null, start outgoing call %s.", call);
                call.startCreateConnection(mPhoneAccountRegistrar);
             else if (activeCall.can(PhoneCapabilities.HOLD)) 
                Log.i(this, "Holding active call %s before start outgoing call %s.",
                        activeCall, call);
                mPendingCallActions.put(activeCall, new PendingCallAction(call,
                        PendingCallAction.PENDING_ACTION_OUTGOING,
                        VideoProfile.VideoState.AUDIO_ONLY));
                activeCall.hold();
             else if (isEmergencyCall) 
                mPendingCallActions.put(activeCall, new PendingCallAction(call,
                        PendingCallAction.PENDING_ACTION_OUTGOING,
                        VideoProfile.VideoState.AUDIO_ONLY));
                activeCall.disconnect();
             else 
                Log.w(this, "Active call not support hold: %s.", activeCall);
                call.disconnect();
            
            /// @
        
    

这里已经过滤掉了无卡、不具备多方通话功能等一些状态,要找到有关于网络、飞行模式的判断还需要往下走。

第5、6、7步:

void process() 
        Log.v(this, "process");
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) 
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        
        adjustAttemptsForConnectionManager();
        //对紧急号码特殊处理
        adjustAttemptsForEmergency();
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
    
  1. adjustAttemptsForEmergency()方法,如果是紧急号码,会为它设置一个专门的账户,先从sim卡中寻找,如果没有就会为他设置一个默认的账户。
  2. attemptNextPhoneAccount方法里通过getService得到ConnectionServiceWrapper(本地Ibinder)的实例。执行了service.createConnection()方法继续进行去电的流程。

第8-21步:

第8步至21步实现“应用层Telecom模块”与“Framwork层Telecom模块”建立连接的过程,同样是通过IPC机制来实现进程间的通讯。

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() 
                    ...

                    if (isConferenceDial || isConferenceInvite) 
                       ...
                     else 
                        mServiceInterface.createConnection(
                                call.getConnectionManagerPhoneAccount(),
                                callId,
                                new ConnectionRequest(
                                        call.getTargetPhoneAccount(),
                                        call.getHandle(),
                                        extras,
                                        call.getVideoState()),
                                call.isIncoming(),
                                call.isUnknown());
                    
                    /// @
                
                ...
        ;

        mBinder.bind(callback);
    

具体如何绑定,不做详述。
1. 通过父类的内部类Binder.bind()方法进行绑定,走到第13步时已经拿到mServiceInterface(Framwork/Telecom模块)这个Binder实例,最后会通过回调方法onSuccess()返回一个绑定成功的信息。
2. 拿到这个binder后,在onSuccess()中立即调用了createConnection()。与Framwork/Telecom模块进行了通讯。

第22-26步:

Framwork/Telecom模块的IConnectionService(Ibinder)类createConnection()被调用,发送广播给ConnectionService服务类执行服务类中的createConnection()方法:

private void (
            final PhoneAccountHandle callManagerAccount,
            final String callId,
            final ConnectionRequest request,
            boolean isIncoming,
            boolean isUnknown) 

       //去电会调用onCreateOutgoingConnection()
        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();

        PhoneAccountHandle handle = connection.getAccountHandle();
        if (handle == null) 
            handle = request.getAccountHandle();
        
        //通过mAdapter通知应用层telecom模块,Conection创建成功。
        mAdapter.handleCreateConnectionComplete(
                callId,
                request,
                new ParcelableConnection(
                        handle,
                        connection.getState(),
                        connection.getCallCapabilities(),
                        connection.getAddress(),
                        connection.getAddressPresentation(),
                        connection.getCallerDisplayName(),
                        connection.getCallerDisplayNamePresentation(),
                        connection.getVideoProvider() == null ?
                                null : connection.getVideoProvider().getInterface(),
                        connection.getVideoState(),
                        connection.isRingbackRequested(),
                        connection.getAudioModeIsVoip(),
                        connection.getStatusHints(),
                        connection.getDisconnectCause(),
                        createConnectionIdList(connection.getConferenceableConnections())));
    

onCreateUnknownConnection()返回当前连接状态,然后通过ConnectionServiceAdapter对象通知所有绑定过的远程binder对象当前的连接状态。
流程图上面第15步至19步就是将远程binder对象储存在ConnectionServiceAdapter.mAdapters集合当中,需要通知的时候,会遍历binder集合,所有binder都会收到信息。

onCreateUnknownConnection方法中对需求中的所有网络连接状态和飞行模式都进行了判断和处理,ConnectionService服务类中的此方法并没有做任何处理,由它的子类(Telephony service 下的TelephonyConnectionService)实现:

@Override
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) 

        Uri handle = request.getAddress();
        //电话的URI为空的情况(eg:TEL:123445)
        if (handle == null) 
            Log.d(this, "onCreateOutgoingConnection, handle is null");
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
                            "No phone number supplied"));
        

        String scheme = handle.getScheme();
        final String number;
        if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) 
           ...
         else 
            ...

            number = handle.getSchemeSpecificPart();
            //号码为空的情况
            if (TextUtils.isEmpty(number)) 
                Log.d(this, "onCreateOutgoingConnection, unable to parse number");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.INVALID_NUMBER,
                                "Unable to parse number"));
            
        

        boolean isEmergencyNumber = PhoneNumberUtils.isPotentialEmergencyNumber(number);

        // Get the right phone object from the account data passed in.
        Phone phone = null;
        //为紧急号码专门设置一个Phone对象
        if (isEmergencyNumber && EmergencyRuleHandler.canHandle()) 
            EmergencyRuleHandler eccHandler = new EmergencyRuleHandler(number);
            phone = eccHandler.getPreferedPhone();
        
        /// @

        if (phone == null) 
            phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
        

        if (phone == null) 
            Log.d(this, "onCreateOutgoingConnection, phone is null");
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.OUTGOING_FAILURE, "Phone is null"));
        

        // Check courrent mode is 4G data only mode.
        if (TelephonyConnectionServiceUtil.getInstance().
                isDataOnlyMode(phone)) 
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.OUTGOING_CANCELED, null));
        
        /// @

        int state = phone.getServiceState().getState();
        boolean useEmergencyCallHelper = false;
    //在飞行模式下拨打的紧急号码,将useEmergencyCallHelper 置为true,这是专门为紧急号码设定的权限,
    //目的是接下来为紧急号码自动关闭飞行模式。(无视飞行模式的存在!)
        if (isEmergencyNumber) 
            if (state == ServiceState.STATE_POWER_OFF) 
                useEmergencyCallHelper = true;
            
         else 
            /* M: call control part start */
            //sim卡关闭的情况,会提示打开sim卡
            if (TelephonyConnectionServiceUtil.getInstance().
                    shouldOpenDataConnection(number, phone)) 
                Log.d(this, "onCreateOutgoingConnection, shouldOpenDataConnection() check fail");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.VOLTE_SS_DATA_OFF,
                                TelecomManagerEx.DISCONNECT_REASON_VOLTE_SS_DATA_OFF));
            
        //飞行模式或者漫游状态下,会提示关闭相应的设置
            if (TelephonyConnectionServiceUtil.getInstance().
                    cellConnMgrShowAlerting(phone.getSubId())) 
                Log.d(this, "onCreateOutgoingConnection, cellConnMgrShowAlerting() check fail");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.OUTGOING_CANCELED_BY_SERVICE,
                                "cellConnMgrShowAlerting() check fail"));
            
            /* M: call control part end */
        //人为因素排除后,会检查当前手机sim卡的网络连接状态
            switch (state) 
                case ServiceState.STATE_IN_SERVICE:   //正常默认
                case ServiceState.STATE_EMERGENCY_ONLY:   //紧急电话模式
                    break;
                case ServiceState.STATE_OUT_OF_SERVICE:    //信号不可用状态
                    return Connection.createFailedConnection(
                            DisconnectCauseUtil.toTelecomDisconnectCause(
                                    android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                    "ServiceState.STATE_OUT_OF_SERVICE"));
                case ServiceState.STATE_POWER_OFF:     //飞行模式状态
                    return Connection.createFailedConnection(
                            DisconnectCauseUtil.toTelecomDisconnectCause(
                                    android.telephony.DisconnectCause.POWER_OFF,
                                    "ServiceState.STATE_POWER_OFF"));
                default:
                    Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
                    return Connection.createFailedConnection(
                            DisconnectCauseUtil.toTelecomDisconnectCause(
                                    android.telephony.DisconnectCause.OUTGOING_FAILURE,
                                    "Unknown service state " + state));
            

            /* M: call control part start */
            if (!canDial(request.getAccountHandle(), number)) 
                Log.d(this, "onCreateOutgoingConnection, canDial() check fail");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.OUTGOING_FAILURE,
                                "canDial() check fail"));
            
            /* M: call control part end */
        

        final TelephonyConnection connection =
                createConnectionFor(phone, null, true /* isOutgoing */);
        if (connection == null) 
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.OUTGOING_FAILURE,
                            "Invalid phone type"));
        

        /* M: call control part start */
        if (isEmergencyNumber) 
            final PhoneAccountHandle phoneAccountHandle =
                new PhoneAccountHandle(mExpectedComponentName, String.valueOf(phone.getSubId()));
            connection.setAccountHandle(phoneAccountHandle);
        
        /* M: call control part end */

        connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
        connection.setInitializing();
        connection.setVideoState(request.getVideoState());
    //对于紧急号码情况的特殊处理,自动关闭飞行模式,并且继续拨打紧急电话。
        if (useEmergencyCallHelper) 
            if (mEmergencyCallHelper == null) 
                mEmergencyCallHelper = new EmergencyCallHelper(this);
            
            final Phone eccPhone = phone;
            mEmergencyCallHelper.startTurnOnRadiosequence(eccPhone,
                    new EmergencyCallHelper.Callback() 
                        @Override
                        public void onComplete(boolean isRadioReady) 
                            if (connection.getState() == Connection.STATE_DISCONNECTED) 
                                // If the connection has already been disconnected, do nothing.
                             else if (isRadioReady) 
                                connection.setInitialized();
                                /* M: call control part start */
                                connection.setEmergency(true);
                                /* M: call control part end */
                                // 拨打电话,进入Framwork层Phone.dial()发起请求
                                placeOutgoingConnection(connection, eccPhone, request);
                             else 
                               //关闭失败?有这种可能吗?什么鬼
                                Log.d(this, "onCreateOutgoingConnection, failed to turn on radio");
                                connection.setDisconnected(
                                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                                android.telephony.DisconnectCause.POWER_OFF,
                                                "Failed to turn on radio."));
                                connection.destroy();
                            
                        
                    );

         else 
            // 拨打电话,进入Framwork层Phone.dial()发起请求
            placeOutgoingConnection(connection, phone, request);
        

        return connection;
    

在这个方法里常见的状态判断有:

  1. 号码为空,我感觉号码如果为空,在拨号盘那边就已经不让过去了,判断了N次了,真是保险;
  2. sim卡关闭,会提示打开sim卡,但是不会继续进行拨号请求了;
  3. 飞行模式或者漫游一类的情况下,会提示关闭相应的设置,同样不会继续发起通话请求;But,紧急号码情况除外,这种情况不会弹出提示框,而是默默地自行关闭设置,然后继续发起请求;
  4. 当前网络模式为STATE_IN_SERVICE(正常模式)或者STATE_EMERGENCY_ONLY(SIM 卡被锁,只能拨打紧急号码)不予处理;
  5. 当前网络模式为STATE_OUT_OF_SERVICE(没注册上网络等等)状态,弹窗提示;
  6. 当前网络模式为STATE_POWER_OFF(飞行模式)状态,弹窗提示;

BUT:与需求不服,需求要求:

  1. 无网络模式需要弹窗提示;
  2. 紧急号码在飞行模式下同样需要提示,不能直接拨打;

无网络模式加一条CASE应该就可以了:

//网络不可用+非紧急号码模式 = 无网络
if(ServiceState.STATE_OUT_OF_SERVICE == state && !phone.getServiceState().isEmergencyOnly())

                       return Connection.createFailedConnection(
                            DisconnectCauseUtil.toTelecomDisconnectCause(
                                    android.telephony.DisconnectCause.NO_SERVICE_CODE,
                                    "ServiceState.STATE_OUT_OF_SERVICE"));

               

这样的确满足了无网络模式的提示,但是后来出现了故障;双卡情况下,当选中的那张卡是无服务,但是另外一张卡有服务时,同样会提示无服务。原来这些网络的判断都是基于选中的那张sim卡为准,后来写了一个专门对双卡进行判断的方法,问题解决。

紧急号码在飞行模式下同样提示关闭飞行模式,由于正常模式的飞行模式提示的代码并不适用于紧急号码的情况,所以在TelephonyConnectionServiceUtil文件下专门添加一条CASE。在onCreateUnknownConnection方法中引用就OK了;

ps:提示关闭相应设置的弹窗在TelephonyConnectionServiceUtil中实现,但是驱动提示语还需要返回Connection对象,继续往下走;

在出现状态异常时,Conection对象会执行createFailedConnection方法:

 public static Connection createFailedConnection(DisconnectCause disconnectCause) 
        return new FailureSignalingConnection(disconnectCause);
    
 private static class FailureSignalingConnection extends Connection 
        public FailureSignalingConnection(DisconnectCause disconnectCause) 
            setDisconnected(disconnectCause);
        
    
 public final void setDisconnected(DisconnectCause disconnectCause) 
        mDisconnectCause = disconnectCause;
        setState(STATE_DISCONNECTED);
        Log.d(this, "Disconnected with cause %s", disconnectCause);
        for (Listener l : mListeners) 
            l.onDisconnected(this, disconnectCause);
        
    
private void setState(int state) 
        if (mState == STATE_DISCONNECTED && mState != state) 
            Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
            return;
        
        if (mState != state) 
            Log.d(this, "setState: %s", stateToString(state));
            mState = state;
            onStateChanged(state);
            for (Listener l : mListeners) 
                l.onStateChanged(this, state);
            
        
    

Connection通过创建FailureSignalingConnection对象的同时,将自身的mState设置成了STATE_DISCONNECTED,mDisconnectCause 设置成 参数disconnectCause;

1.mState如果等于STATE_DISCONNECTED,在第30步会有判断;
2.mDisconnectCause 的属性就是弹窗的提示语。

第30步:

27-29步忽略,跨进程传递而已。

private void handleCreateConnectionComplete(
            String callId,
            ConnectionRequest request,
            ParcelableConnection connection) 
       //验证了上面说的,只要有异常情况,Connection都会讲自身状态改成STATE_DISCONNECTED
        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 
            // Successful connection
            //正常情况下的处理
            if (mPendingResponses.containsKey(callId)) 
                mPendingResponses.remove(callId)
                        .handleCreateConnectionSuccess(mCallIdMapper, connection);
            
        
    

因此当Connection有异常时,会调用removeCall();

第31步:

void removeCall(Call call, DisconnectCause disconnectCause) 

    //CreateConnectionResponse 的实现类是Call
        CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
        if (response != null) 
            response.handleCreateConnectionFailure(disconnectCause);
        

        mCallIdMapper.removeCall(call);
    

第33步:

第32步省略。

void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) 

        /// M: for volte @
        // TODO: if disconnectCause is IMS_EMERGENCY_REREG, redial it and do not notify disconnect.
//        placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
        /// @

        call.setDisconnectCause(disconnectCause);
        //将当前Call的State设置成CallState.DISCONNECTED
        setCallState(call, CallState.DISCONNECTED);
    

Call的状态最终会传递到IuCallUI进程下。是是否需要弹窗的标志。

第37步:

前面几部省略。

    private void updateCall(Call call) 
        /// M: for performance enhancement, reduce CONNECTING state update. @
        if (call.getState() == CallState.CONNECTING && call == mCall
                && call.getState() == mCallState) 
            return;
        
        mCall = call;
        mCallState = call.getState();
        /// @
        if (!mInCallServices.isEmpty()) 
            for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) 
                ComponentName componentName = entry.getKey();
                IInCallService inCallService = entry.getValue();
                ParcelableCall parcelableCall = toParcelableCall(call,
                        componentName.equals(mInCallComponentName) /* includeVideoProvider */);

                Log.v(this, "updateCall %s ==> %s", call, parcelableCall);
                try 
                    inCallService.updateCall(parcelableCall);
                 catch (RemoteException ignored) 
                
            
        
    

远程传递parcelableCall。

第45步:

直接看第45步,前面都是传递加监听各种处理。

private void updateFromTelecommCall() 
        Log.d(this, "updateFromTelecommCall: " + mTelecommCall);
        setState(translateState(mTelecommCall.getState()));
        setDisconnectCause(mTelecommCall.getDetails().getDisconnectCause());

        if (mTelecommCall.getVideoCall() != null) 
            if (mVideoCallListener == null) 
                mVideoCallListener = new InCallVideoCallListener(this);
            
            mTelecommCall.getVideoCall().setVideoCallListener(mVideoCallListener);
        

        mChildCallIds.clear();
        for (int i = 0; i < mTelecommCall.getChildren().size(); i++) 
            mChildCallIds.add(
                    CallList.getInstance().getCallByTelecommCall(
                            mTelecommCall.getChildren().get(i)).getId());
        
    

将传递过来的参数全部赋值给自身属性,此时状态都是STATE_DISCONNECTED。

第46、47、48步:

public void onDisconnect(Call call) 
        if (updateCallInMap(call)) 
            Log.i(this, "onDisconnect: " + call);
            // update local call list
            updateIncomingCallList(call);
            updateHoldCallList(call);
            // notify those listening for changes on this specific change
            notifyCallUpdateListeners(call);
            // notify those listening for all disconnects
            notifyListenersOfDisconnect(call);
        
    
private void notifyListenersOfDisconnect(Call call) 
        for (Listener listener : mListeners) 
            listener.onDisconnect(call);
        
    

第49步:

private void maybeShowErrorDialogOnDisconnect(Call call) 
        // For newly disconnected calls, we may want to show a dialog on specific error conditions
        Log.d(this, "call.getState(): " + call.getState());
        if (isActivityStarted() && call.getState() == Call.State.DISCONNECTED) 
            if (call.getAccountHandle() == null && !call.isConferenceCall()) 
                setDisconnectCauseForMissingAccounts(call);
            
            Log.d(this, "call.getDisconnectCause : " + call.getDisconnectCause());
            mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause());
            /// M: For ALPS01798961. @
         else if (!isActivityStarted() && call.getState() == Call.State.DISCONNECTED) 
            mDisconnectCause = call.getDisconnectCause();
        
        /// @
    

最终在这个方法里对状态进行判断,只有当Call的State为DISCONNECTED时才会弹出提醒。之后就是InCallActivity去显示啦。显示的内容一直是之前DisconnectCause的属性。

总结:

在对各种状态进行判断的时候,需要将要显示的字串复制给DisconnectCause,最终就显示出来了。
写了好久,纠结。提醒自己,万物基于源代码…

以上是关于(学习笔记)android 5.0 系统去电流程状态判断(下)的主要内容,如果未能解决你的问题,请参考以下文章

(学习笔记)android 5.0 系统去电流程状态判断(下)

(学习笔记)android 5.0 系统去电流程状态判断(下)

Tiny4412 Android 5.0 编译系统学习笔记

Android 5.0 Camera系统源码分析:Camera预览3A流程

5.0以上机器XPOSED框架安装流程

无论如何要为Android中的来电和去电制作我的自定义用户界面吗?