(九十八)Android O 探討WiFi先開啟後再開啟WiFi熱點繼而改變配置的情況
1.場景
- 開啟WiFi
- 開啟WiFi熱點(這時會把WiFi關閉)
- 改變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開啟狀態。