1. 程式人生 > >(九十八)Android O 探討WiFi先開啟後再開啟WiFi熱點繼而改變配置的情況

(九十八)Android O 探討WiFi先開啟後再開啟WiFi熱點繼而改變配置的情況

1.場景

  1. 開啟WiFi
  2. 開啟WiFi熱點(這時會把WiFi關閉)
  3. 改變WiFi熱點的配置(這時會先關閉WiFi熱點,再開啟WiFi,最後再開啟WiFi熱點)

探討下第3步WiFi和WiFi熱點交替開啟的邏輯實現。

2.流程梳理

TetherSettings.java

這個類是負責WiFi熱點屬性改變後的介面邏輯處理的,如下程式碼所示,改變配置點選儲存後,這邊會判斷下如果mWifiConfig不為空並且當前WiFi熱點為開啟狀態,那麼先停止WiFi熱點再開啟WiFi熱點,停止是在這個onClick裡實現的,但重啟WiFi熱點這個onClick方法只是記了一個標誌位。重啟WiFi熱點的邏輯是當接收到WiFi熱點已關閉的廣播後再判斷下標誌位再重啟WiFi熱點。

    public void onClick(DialogInterface dialogInterface, int button) {
        if (button == DialogInterface.BUTTON_POSITIVE) {
            mWifiConfig = mDialog.getConfig();
            if (mWifiConfig != null) {
                /**
                 * if soft AP is stopped, bring up
                 * else restart with new config
                 * TODO: update config on a running access point when framework support is added
                 */
                if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
                    Log.d("TetheringSettings",
                            "Wifi AP config changed while enabled, stop and restart");
                    mRestartWifiApAfterConfigChange = true;
                    mCm.stopTethering(TETHERING_WIFI);
                }
                mWifiManager.setWifiApConfiguration(mWifiConfig);
                int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
                mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
                        mWifiConfig.SSID,
                        mSecurityType[index]));
            }
        }
    }



   private class TetherChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context content, Intent intent) {
            String action = intent.getAction();
...
            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
                if (state == WifiManager.WIFI_AP_STATE_DISABLED
                        && mRestartWifiApAfterConfigChange) {
                    mRestartWifiApAfterConfigChange = false;
                    Log.d(TAG, "Restarting WifiAp due to prior config change.");
                    startTethering(TETHERING_WIFI);
                }
            } 

那麼WiFi熱點關閉時WiFi為何會自動開啟呢?

WifiController

WiFi開啟時所處狀態

    class StaEnabledState extends State {
        @Override
        public void enter() {
            mWifiStateMachine.setSupplicantRunning(true);
        }
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
...
                case CMD_SET_AP:
                    if (msg.arg1 == 1) {
                        // remeber that we were enabled
                        mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED);
                        deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
                        transitionTo(mApStaDisabledState);
                    }
                    break;

這時候如果開啟WiFi熱點,那麼WifiController就會記住當前WiFi狀態為開啟狀態,並將開啟WiFi熱點的訊息委託給ApStaDisabledState,就和WiFi沒開啟時邏輯一樣,當然進入到ApStaDisabledState狀態會將supplicant幹掉,也就是WiFi會先關閉。

    class ApStaDisabledState extends State {
        private int mDeferredEnableSerialNumber = 0;
        private boolean mHaveDeferredEnable = false;
        private long mDisabledTimestamp;

        @Override
        public void enter() {
            mWifiStateMachine.setSupplicantRunning(false);
            // Supplicant can't restart right away, so not the time we switched off
            mDisabledTimestamp = SystemClock.elapsedRealtime();
            mDeferredEnableSerialNumber++;
            mHaveDeferredEnable = false;
            mWifiStateMachine.clearANQPCache();
        }

WiFi熱點開啟以後會進入如下ApEnabledState狀態,表示WiFi熱點已開啟

    /**
     * Only transition out of this state when AP failed to start or AP is stopped.
     */
    class ApEnabledState extends State {
        /**
         * Save the pending state when stopping the AP, so that it will transition
         * to the correct state when AP is stopped.  This is to avoid a possible
         * race condition where the new state might try to update the driver/interface
         * state before AP is completely torn down.
         */
        private State mPendingState = null;

        /**
         * Determine the next state based on the current settings (e.g. saved
         * wifi state).
         */
        private State getNextWifiState() {
            if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) {
                return mDeviceActiveState;
            }

            if (mSettingsStore.isScanAlwaysAvailable()) {
                return mStaDisabledWithScanState;
            }

            return mApStaDisabledState;
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                ...
                case CMD_SET_AP:
                    if (msg.arg1 == 0) {
                        mWifiStateMachine.setHostApRunning(null, false);
                        mPendingState = getNextWifiState();
                    }
                    break;
                case CMD_AP_STOPPED:
                    if (mPendingState == null) {
                        /**
                         * Stop triggered internally, either tether notification
                         * timed out or wifi is untethered for some reason.
                         */
                        mPendingState = getNextWifiState();
                    }
                    if (mPendingState == mDeviceActiveState && mDeviceIdle) {
                        checkLocksAndTransitionWhenDeviceIdle();
                    } else {
                        // go ahead and transition because we are not idle or we are not going
                        // to the active state.
                        transitionTo(mPendingState);
                    }
                    break;

這時候再接收到關閉WiFi熱點的訊息,這時會先將WiFi熱點關掉,然後判斷一下後續切換的狀態。


        /**
         * Determine the next state based on the current settings (e.g. saved
         * wifi state).
         */
        private State getNextWifiState() {
            if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) {
                return mDeviceActiveState;
            }

            if (mSettingsStore.isScanAlwaysAvailable()) {
                return mStaDisabledWithScanState;
            }

            return mApStaDisabledState;
        }

由於最開始WiFi是處於開啟狀態並且已經記錄了,所以這時候狀態會切換到mDeviceActiveState。

切換流程是接收到WiFi熱點已關閉的廣播,然後發出CMD_AP_STOPPED訊息並完成切換

        mContext.registerReceiver(
                new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        String action = intent.getAction();
...
                        } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
                            int state = intent.getIntExtra(
                                    WifiManager.EXTRA_WIFI_AP_STATE,
                                    WifiManager.WIFI_AP_STATE_FAILED);
                            if (state == WifiManager.WIFI_AP_STATE_FAILED) {
                                loge(TAG + "SoftAP start failed");
                                sendMessage(CMD_AP_START_FAILURE);
                            } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
                                sendMessage(CMD_AP_STOPPED);
                            }
                case CMD_AP_STOPPED:
                    if (mPendingState == null) {
                        /**
                         * Stop triggered internally, either tether notification
                         * timed out or wifi is untethered for some reason.
                         */
                        mPendingState = getNextWifiState();
                    }
                    if (mPendingState == mDeviceActiveState && mDeviceIdle) {
                        checkLocksAndTransitionWhenDeviceIdle();
                    } else {
                        // go ahead and transition because we are not idle or we are not going
                        // to the active state.
                        transitionTo(mPendingState);
                    }
                    break;

切換到DeviceActiveState的enter方法,這邊就開始了WiFi開啟流程了。

   /* Parent: StaEnabledState */
    class DeviceActiveState extends State {
        @Override
        public void enter() {
            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
            mWifiStateMachine.setHighPerfModeEnabled(false);
        }

3.總結

1代表開啟WiFi熱點,從WiFi開啟狀態先切換到熱點關閉狀態,開啟WiFi熱點的操作留給第2步

2代表處理開啟WiFi熱點的邏輯,從熱點關閉狀態切換到熱點開啟狀態

3代表關閉WiFi熱點,從熱點開啟狀態切換到WiFi開啟狀態。