(九十八)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打开状态。