浅析网络轮询状态的变化---pollState()

前言

Android N

Log分析

04-13 16:47:03.475 D/RILJ ( 2168): [8442]> DATA_REGISTRATION_STATE [SUB0] 
04-13 16:47:03.651 D/RILJ ( 2168): [8442]< DATA_REGISTRATION_STATE {1, null, 02f0b403, 14, null, 20, 10410, null, null, null, null} [SUB0] 

一般我们会看到这样注册的Log,但是这个注册的流程是怎样的呢?我们先来看注册流程。

注册流程

前面UICC模块的我们就跳过了,想了解的小伙伴儿自己查资料了解一下,直接到ICC Change开始。
在ServiceStateTracker.java中:

1、注册EVENT_ICC_CHANGED

    public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
        mPhone = phone;
        mCi = ci;
        mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
    }

2、监听EVENT_ICC_CHANGED

    public void handleMessage(Message msg) {
        AsyncResult ar;
        int[] ints;
        Message message;
        switch (msg.what) {
            case EVENT_ICC_CHANGED:
                onUpdateIccAvailability();
                break;
        }
    }

3、调用onUpdateIccAvailability()

    protected void onUpdateIccAvailability() {
        if (mPhone.isPhoneTypeGsm()) {
            mUiccApplcation.registerForReady(this, EVENT_SIM_READY, null);
            if (mIccRecords != null) {
                mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
            }
        }
    }

4、监听EVENT_SIM_READY

            case EVENT_SIM_READY:
                // Reset the mPreviousSubId so we treat a SIM power bounce
                // as a first boot.  See b/19194287
                mOnSubscriptionsChangedListener.mPreviousSubId.set(-1);
                pollState();
                // Signal strength polling stops when radio is off
                queueNextSignalStrengthPoll();
                break;

5、调用pollState()

    /**
     * A complete "service state" from our perspective is
     * composed of a handful of separate requests to the radio.
     *
     * We make all of these requests at once, but then abandon them
     * and start over again if the radio notifies us that some
     * event has changed
     */
    public void pollState() {
        pollState(false);
    }
    /**
     * We insist on polling even if the radio says its off.
     * Used when we get a network changed notification
     * but the radio is off - part of iwlan hack
     */
    private void modemTriggeredPollState() {
        pollState(true);
    }

这里需要注意一下,这里有两个调用pollState(boolean XXX) 的地方,看函数名也知道:
1)第一个函数是在插拔卡的时候会调用;
2)第二个函数是在网络变化的时候时候调用的。

如下:

        mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
            case EVENT_NETWORK_STATE_CHANGED:
                modemTriggeredPollState();
                break;

6、接着继续调用有参数的pollState(boolean modemTriggered)

    public void pollState(boolean modemTriggered) {
        mPollingContext = new int[1];
        mPollingContext[0] = 0;

        switch (mCi.getRadioState()) {
            case RADIO_UNAVAILABLE:
                ... ...
            case RADIO_OFF:
                ... ...
            default:
                if (modemTriggered) mIsModemTriggeredPollingPending = true;
                // Issue all poll-related commands at once then count down the responses, which
                // are allowed to arrive out-of-order
                mPollingContext[0]++;
                mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));

                mPollingContext[0]++;
                //获取数据注册状态
                mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));

                mPollingContext[0]++;
                //获取语音注册状态
                mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION,
                        mPollingContext));

                if (mPhone.isPhoneTypeGsm()) {
                    mPollingContext[0]++;
                    mCi.getNetworkSelectionMode(obtainMessage(
                            EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
                }
                break;
        }
    }

7、监听EVENT_POLL_STATE_GPRS等

            case EVENT_POLL_STATE_REGISTRATION:
            case EVENT_POLL_STATE_GPRS:
            case EVENT_POLL_STATE_OPERATOR:
                ar = (AsyncResult) msg.obj;
                handlePollStateResult(msg.what, ar);
                break;

8、调用handlePollStateResult()

    protected void handlePollStateResult(int what, AsyncResult ar) {
                ... ...
                pollStateDone();
    }

9、轮询状态完成pollStateDone()

    private void pollStateDone() {
        mIsModemTriggeredPollingPending = false;
        if (mPhone.isPhoneTypeGsm()) {
            pollStateDoneGsm();
        } else if (mPhone.isPhoneTypeCdma()) {
            pollStateDoneCdma();
        } else {
            pollStateDoneCdmaLte();
        }
    }

10、分网络类型调用,如pollStateDoneGsm()

    protected GsmCdmaPhone mPhone;
    private void pollStateDoneGsm() {
                mPhone.notifyServiceStateChanged(mSS);
    }

11、通知GsmCdmaPhone等变化

在GsmCdmaPhone.java中:

public class GsmCdmaPhone extends Phone {
    ... ...
    public void notifyServiceStateChanged(ServiceState ss) {
        super.notifyServiceStateChangedP(ss);
    }
    ... ...
}

Phone.java

    protected PhoneNotifier mNotifier;
    protected void notifyServiceStateChangedP(ServiceState ss) {
        AsyncResult ar = new AsyncResult(null, ss, null);
        mServiceStateRegistrants.notifyRegistrants(ar);

        mNotifier.notifyServiceState(this);
    }

DefaultPhoneNotifier.java

public class DefaultPhoneNotifier implements PhoneNotifier {
    protected ITelephonyRegistry mRegistry;
    @Override
    public void notifyServiceState(Phone sender) {
        ServiceState ss = sender.getServiceState();
        int phoneId = sender.getPhoneId();
        int subId = sender.getSubId();

        Rlog.d(LOG_TAG, "nofityServiceState: mRegistry=" + mRegistry + " ss=" + ss
                + " sender=" + sender + " phondId=" + phoneId + " subId=" + subId);
        if (ss == null) {
            ss = new ServiceState();
            ss.setStateOutOfService();
        }
        try {
            if (mRegistry != null) {
                //在这里继续调用
                mRegistry.notifyServiceStateForPhoneId(phoneId, subId, ss);
            }
        } catch (RemoteException ex) {
            // system process is dead
        }
    }
}

TelephonyRegistry.java

class TelephonyRegistry extends ITelephonyRegistry.Stub {
    ... ...
    public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) {
        if (!checkNotifyPermission("notifyServiceState()")){
            return;
        }

        synchronized (mRecords) {
            if (VDBG) {
                log("notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId
                    + " state=" + state);
            }
            if (validatePhoneId(phoneId)) {
                mServiceState[phoneId] = state;
                logServiceStateChanged("notifyServiceStateForSubscriber", subId, phoneId, state);
                if (VDBG) toStringLogSSC("notifyServiceStateForSubscriber");

                for (Record r : mRecords) {
                    if (VDBG) {
                        log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
                                + " phoneId=" + phoneId + " state=" + state);
                    }
                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
                            idMatch(r.subId, subId, phoneId)) {
                        try {
                            if (DBG) {
                                log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
                                        + " subId=" + subId + " phoneId=" + phoneId
                                        + " state=" + state);
                            }
                            //在这里回调
                            r.callback.onServiceStateChanged(new ServiceState(state));
                        } catch (RemoteException ex) {
                            mRemoveList.add(r.binder);
                        }
                    }
                }
            } else {
                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
            }
            handleRemoveListLocked();
        }
        //发出广播,通知Service state改变了
        broadcastServiceStateChanged(state, phoneId, subId);
    }
}

12、最后在PhoneStateListener()中回调

    private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
        private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;

        public IPhoneStateListenerStub(PhoneStateListener phoneStateListener) {
            mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
        }

        private void send(int what, int arg1, int arg2, Object obj) {
            PhoneStateListener listener = mPhoneStateListenerWeakRef.get();
            if (listener != null) {
                Message.obtain(listener.mHandler, what, arg1, arg2, obj).sendToTarget();
            }
        }

        public void onServiceStateChanged(ServiceState serviceState) {
            send(LISTEN_SERVICE_STATE, 0, 0, serviceState);
        }
        ... ...
    }

监听:

    public PhoneStateListener(int subId, Looper looper) {
        mSubId = subId;
        mHandler = new Handler(looper) {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case LISTEN_SERVICE_STATE:
                        PhoneStateListener.this.onServiceStateChanged((ServiceState)msg.obj);
                        break;
                        ... ...
                    }
                    ... ...

13、轮询过程中存在的问题

在做某些测试的时候,会出现不显示4G图标,并且不能上网的情况。
情况简述:
开关飞行模式,状态栏不显示4G图标且不能上网。

简要分析:
这个跟系统时序有关,pollstate更新事件会导致mPollingContext值被覆盖,从而可能导致之前的data注册状态信息无法更新到Telephony中。
如果开关飞行模式时间延长一点,应该就不会有问题。

//开关飞行模式

04-23 10:39:33.078 D/RILJ    ( 2149): [3581]> RADIO_POWER off [SUB0]
04-23 10:39:33.185 D/RILJ    ( 2149): [3586]> RADIO_POWER off [SUB1]
04-23 10:39:34.781 D/RILJ    ( 2149): [3581]< RADIO_POWER  [SUB0]
04-23 10:39:34.824 D/RILJ    ( 2149): [3586]< RADIO_POWER  [SUB1]

04-23 10:39:35.737 D/RILJ    ( 2149): [3610]> RADIO_POWER on [SUB0]
04-23 10:39:35.742 D/RILJ    ( 2149): [3611]> RADIO_POWER on [SUB1]
04-23 10:39:35.834 D/RILJ    ( 2149): [3610]< RADIO_POWER  [SUB0]
04-23 10:39:35.854 D/RILJ    ( 2149): [3611]< RADIO_POWER  [SUB1]

04-23 10:39:36.027 D/SST     ( 2149): EVENT_RADIO_POWER_OFF_DONE
04-23 10:39:36.493 D/SST     ( 2149): EVENT_RADIO_POWER_OFF_DONE

//打开飞行模式前的一次状态上报

04-23 10:39:33.168 D/RILJ    ( 2149): [3583]< DATA_REGISTRATION_STATE {1, null, 02f0b403, 14, null, 20, 10410, 61, 49329155, null, null} [SUB0]

//飞行模式之后的驻上网时间

04-23 10:39:35.940 D/RILJ    ( 2149): [3619]< DATA_REGISTRATION_STATE {2, null, null, null, 0, 20, null, null, null, null, null} [SUB0]
//... ...(省略11次)
04-23 10:39:38.775 D/RILJ    ( 2149): [3710]< DATA_REGISTRATION_STATE {1, null, 02f0b403, 14, null, 20, 10410, 61, 49329155, null, null} [SUB0]

虽然卡1上报了13次data状态变化消息(包含从无服务到有服务的过程),但最终只有最后一次的状态变化真正更新到telephony中,而这个状态刚好跟打开飞行模式之前的状态一致。

1)一旦进入pollstate函数中,就会重新构造mPollingContext

    public void pollState(boolean modemTriggered) {
        mPollingContext = new int[1];
        mPollingContext[0] = 0;
    ... ...
    }

2)检查mPollingContext是否与当前一致:
在处理DATA_REGISTRATION_STATE事件时,会先检查发起request的mPollingContext跟当前的mPollingContext是否一致,如果不一致,直接跳过了;
所以导致data注册状态变化消息无法更新的原因就是:mPollingContext覆盖

    protected void handlePollStateResult(int what, AsyncResult ar) {
        // Ignore stale requests from last poll
        if (ar.userObj != mPollingContext) return;
        ... ...
    }

3)系统时序的时间差分析
//打开飞行模式后,底层上报radio off,然后会发通知;

04-23 10:39:34.811 D/RILJ    ( 2149): [UNSL]< UNSOL_RESPONSE_RADIO_STATE_CHANGED RADIO_OFF [SUB0]

//收到通知处理状态改变事件时,可以看到这时获取的radio state是RADIO_ON;

04-23 10:39:35.917 D/SST     ( 2149): pollState: radio state =RADIO_ON mDeviceShuttingDown = false

(此处的Log需要自己在pollState()中自行添加。)

//这是因为在10:39:35.917时间点之前已经关闭飞行模式,底层已经上报RADIO_ON的状态了。

04-23 10:39:35.908 D/RILJ    ( 2149): [UNSL]< UNSOL_RESPONSE_RADIO_STATE_CHANGED RADIO_ON [SUB0]

这种为时序性、概率性问题,比较难优化,所以建议在开关飞行模式时间延长几秒钟。


分析到这里就结束了,分析得比较粗浅,哪里分析不对的,希望各路大神可以指出来,谢谢~


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值