diff options
author | David Su <dysu@google.com> | 2019-09-10 17:13:13 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-09-10 17:13:13 +0000 |
commit | 14de907e0aa62a1a2f8e98a60171992c83d9ab42 (patch) | |
tree | d271c10755f060d41ddc1315b2f0d5570ac76036 /service | |
parent | 2a070a6599e3065d556bc47651502de8fab77a56 (diff) | |
parent | c0678d4a363c62233c64e024adb01c643f5c2438 (diff) |
Merge changes from topic "merge-wc-amw"
* changes:
ActiveModeWarden: ensure wifi is re-enabled if disabled by ECM
WifiController: Only go to scan mode if we are NOT in airplane mode
Fixed WifiController test cases that were moved to ActiveModeWardenTest
Moved test cases from WifiControllerTest into ActiveModeWardenTest
ActiveModeWarden refactor: Fixed all unit tests except WifiControllerTest
ActiveModeWarden: made EcmState into an abstract base state
ActiveModeWarden: Clean up code so that code compiles
Merged WifiController and ActiveModeWarden.ModeStateMachine
WifiController: Inline callbacks that are only used once
Move WifiController into ActiveModeWarden as an inner class
Clean up ActiveModeWarden before merging WifiController
Migrate WifiServiceImpl to use WifiThreadRunner
Create helper class to run code on main Wifi thread
Diffstat (limited to 'service')
17 files changed, 867 insertions, 1035 deletions
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java index 9a8abdf0c..4f1a00371 100644 --- a/service/java/com/android/server/wifi/ActiveModeWarden.java +++ b/service/java/com/android/server/wifi/ActiveModeWarden.java @@ -17,7 +17,11 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.location.LocationManager; import android.net.wifi.WifiManager; import android.os.BatteryStats; import android.os.Handler; @@ -27,16 +31,19 @@ import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.IState; import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.wifi.WifiNative.StatusListener; +import com.android.server.wifi.util.WifiPermissionsUtil; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collection; /** * This class provides the implementation for different WiFi operating modes. @@ -45,64 +52,25 @@ public class ActiveModeWarden { private static final String TAG = "WifiActiveModeWarden"; private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED"; - private ModeStateMachine mModeStateMachine; - // Holder for active mode managers private final ArraySet<ActiveModeManager> mActiveModeManagers; // DefaultModeManager used to service API calls when there are not active mode managers. - private DefaultModeManager mDefaultModeManager; + private final DefaultModeManager mDefaultModeManager; private final WifiInjector mWifiInjector; - private final Context mContext; private final Looper mLooper; private final Handler mHandler; - private final WifiNative mWifiNative; + private final Context mContext; + private final ClientModeImpl mClientModeImpl; + private final WifiSettingsStore mSettingsStore; + private final FrameworkFacade mFacade; + private final WifiPermissionsUtil mWifiPermissionsUtil; private final IBatteryStats mBatteryStats; - private final SelfRecovery mSelfRecovery; - private BaseWifiDiagnostics mWifiDiagnostics; private final ScanRequestProxy mScanRequestProxy; - - // The base for wifi message types - static final int BASE = Protocol.BASE_WIFI; - - // The message identifiers below are mapped to those in ClientModeImpl when applicable. - // Start the soft access point - static final int CMD_START_AP = BASE + 21; - // Indicates soft ap start failed - static final int CMD_START_AP_FAILURE = BASE + 22; - // Stop the soft access point - static final int CMD_STOP_AP = BASE + 23; - // Soft access point teardown is completed - static final int CMD_AP_STOPPED = BASE + 24; - - // Start Scan Only mode - static final int CMD_START_SCAN_ONLY_MODE = BASE + 200; - // Indicates that start Scan only mode failed - static final int CMD_START_SCAN_ONLY_MODE_FAILURE = BASE + 201; - // Indicates that scan only mode stopped - static final int CMD_STOP_SCAN_ONLY_MODE = BASE + 202; - // ScanOnly mode teardown is complete - static final int CMD_SCAN_ONLY_MODE_STOPPED = BASE + 203; - // ScanOnly mode failed - static final int CMD_SCAN_ONLY_MODE_FAILED = BASE + 204; - - // Start Client mode - static final int CMD_START_CLIENT_MODE = BASE + 300; - // Indicates that start client mode failed - static final int CMD_START_CLIENT_MODE_FAILURE = BASE + 301; - // Indicates that client mode stopped - static final int CMD_STOP_CLIENT_MODE = BASE + 302; - // Client mode teardown is complete - static final int CMD_CLIENT_MODE_STOPPED = BASE + 303; - // Client mode failed - static final int CMD_CLIENT_MODE_FAILED = BASE + 304; - - private StatusListener mWifiNativeStatusListener; + private final WifiController mWifiController; private WifiManager.SoftApCallback mSoftApCallback; private WifiManager.SoftApCallback mLohsCallback; - private ScanOnlyModeManager.Listener mScanOnlyCallback; - private ClientModeManager.Listener mClientModeCallback; /** * Called from WifiServiceImpl to register a callback for notifications from SoftApManager @@ -119,59 +87,107 @@ public class ActiveModeWarden { mLohsCallback = callback; } - /** - * Called from WifiController to register a callback for notifications from ScanOnlyModeManager - */ - public void registerScanOnlyCallback(@NonNull ScanOnlyModeManager.Listener callback) { - mScanOnlyCallback = callback; - } - - /** - * Called from WifiController to register a callback for notifications from ClientModeManager - */ - public void registerClientModeCallback(@NonNull ClientModeManager.Listener callback) { - mClientModeCallback = callback; - } - ActiveModeWarden(WifiInjector wifiInjector, - Context context, Looper looper, WifiNative wifiNative, DefaultModeManager defaultModeManager, - IBatteryStats batteryStats) { + IBatteryStats batteryStats, + BaseWifiDiagnostics wifiDiagnostics, + Context context, + ClientModeImpl clientModeImpl, + WifiSettingsStore settingsStore, + FrameworkFacade facade, + WifiPermissionsUtil wifiPermissionsUtil) { mWifiInjector = wifiInjector; - mContext = context; mLooper = looper; mHandler = new Handler(looper); - mWifiNative = wifiNative; + mContext = context; + mClientModeImpl = clientModeImpl; + mSettingsStore = settingsStore; + mFacade = facade; + mWifiPermissionsUtil = wifiPermissionsUtil; mActiveModeManagers = new ArraySet<>(); mDefaultModeManager = defaultModeManager; mBatteryStats = batteryStats; - mSelfRecovery = mWifiInjector.getSelfRecovery(); - mWifiDiagnostics = mWifiInjector.getWifiDiagnostics(); - mScanRequestProxy = mWifiInjector.getScanRequestProxy(); - mModeStateMachine = new ModeStateMachine(); - mWifiNativeStatusListener = new WifiNativeStatusListener(); - mWifiNative.registerStatusListener(mWifiNativeStatusListener); + mScanRequestProxy = wifiInjector.getScanRequestProxy(); + mWifiController = new WifiController(); + + wifiNative.registerStatusListener(isReady -> { + if (!isReady) { + mHandler.post(() -> { + Log.e(TAG, "One of the native daemons died. Triggering recovery"); + wifiDiagnostics.captureBugReportData( + WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); + + // immediately trigger SelfRecovery if we receive a notice about an + // underlying daemon failure + // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is + // instantiated after ActiveModeWarden, so use WifiInjector to get the instance + // instead of directly passing in SelfRecovery in the constructor. + mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); + }); + } + }); } - /** - * Method to switch wifi into client mode where connections to configured networks will be - * attempted. - */ - public void enterClientMode() { - changeMode(ModeStateMachine.CMD_START_CLIENT_MODE); + /** Begin listening to broadcasts and start the internal state machine. */ + public void start() { + mWifiController.start(); + } + + /** Disable Wifi for recovery purposes. */ + public void recoveryDisableWifi() { + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); } /** - * Method to switch wifi into scan only mode where network connection attempts will not be made. - * - * This mode is utilized by location scans. If wifi is disabled by a user, but they have - * previously configured their device to perform location scans, this mode allows wifi to - * fulfill the location scan requests but will not be used for connectivity. + * Restart Wifi for recovery purposes. + * @param reason One of {@link SelfRecovery.RecoveryReason} */ - public void enterScanOnlyMode() { - changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE); + public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason) { + mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); + } + + /** Wifi has been toggled. */ + public void wifiToggled() { + mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED); + } + + /** Airplane Mode has been toggled. */ + public void airplaneModeToggled() { + mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED); + } + + /** Starts SoftAp. */ + public void startSoftAp(SoftApModeConfiguration softApConfig) { + mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, softApConfig); + } + + /** Stop SoftAp. */ + public void stopSoftAp(int mode) { + mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode); + } + + /** Emergency Callback Mode has changed. */ + public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) { + mWifiController.sendMessage( + WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0); + } + + /** Emergency Call state has changed. */ + public void emergencyCallStateChanged(boolean isInEmergencyCall) { + mWifiController.sendMessage( + WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0); + } + + /** Scan always mode has changed. */ + public void scanAlwaysModeChanged() { + mWifiController.sendMessage(WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED); + } + + /** When SoftAp has stopped. */ + public void softApStopped() { + mWifiController.sendMessage(ActiveModeWarden.WifiController.CMD_AP_STOPPED); } /** @@ -181,9 +197,20 @@ public class ActiveModeWarden { * the persisted config is to be used) and the target operating mode (ex, * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). * + * @param softApConfig SoftApModeConfiguration for the hostapd softap */ - public void enterSoftAPMode(@NonNull SoftApModeConfiguration softApConfig) { - mHandler.post(() -> startSoftAp(softApConfig)); + private void enterSoftAPMode(@NonNull SoftApModeConfiguration softApConfig) { + mHandler.post(() -> { + Log.d(TAG, "Starting SoftApModeManager config = " + + softApConfig.getWifiConfiguration()); + + SoftApCallbackImpl callback = new SoftApCallbackImpl(softApConfig.getTargetMode()); + ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softApConfig); + callback.setActiveModeManager(manager); + manager.start(); + mActiveModeManagers.add(manager); + updateBatteryStatsWifiState(true); + }); } /** @@ -196,7 +223,7 @@ public class ActiveModeWarden { * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). * Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs. */ - public void stopSoftAPMode(int mode) { + private void stopSoftAPMode(int mode) { mHandler.post(() -> { for (ActiveModeManager manager : mActiveModeManagers) { if (!(manager instanceof SoftApManager)) continue; @@ -213,18 +240,9 @@ public class ActiveModeWarden { } /** - * Method to disable wifi in sta/client mode scenarios. - * - * This mode will stop any client/scan modes and will not perform any network scans. - */ - public void disableWifi() { - changeMode(ModeStateMachine.CMD_DISABLE_WIFI); - } - - /** * Method to stop all active modes, for example, when toggling airplane mode. */ - public void shutdownWifi() { + private void shutdownWifi() { mHandler.post(() -> { for (ActiveModeManager manager : mActiveModeManagers) { manager.stop(); @@ -236,24 +254,33 @@ public class ActiveModeWarden { /** * Dump current state for active mode managers. * - * Must be called from ClientModeImpl thread. + * Must be called from the main Wifi thread. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Dump of " + TAG); - pw.println("Current wifi mode: " + getCurrentMode()); pw.println("NumActiveModeManagers: " + mActiveModeManagers.size()); for (ActiveModeManager manager : mActiveModeManagers) { manager.dump(fd, pw, args); } + mWifiController.dump(fd, pw, args); + } + + @VisibleForTesting + String getCurrentMode() { + IState state = mWifiController.getCurrentState(); + return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName(); } - protected String getCurrentMode() { - return mModeStateMachine.getCurrentMode(); + @VisibleForTesting + Collection<ActiveModeManager> getActiveModeManagers() { + return new ArraySet<>(mActiveModeManagers); } - private void changeMode(int newMode) { - mModeStateMachine.sendMessage(newMode); + @VisibleForTesting + boolean isInEmergencyMode() { + IState state = mWifiController.getCurrentState(); + return ((WifiController.BaseState) state).isInEmergencyMode(); } /** @@ -271,67 +298,329 @@ public class ActiveModeWarden { } } - private class ModeStateMachine extends StateMachine { - // Commands for the state machine - these will be removed, - // along with the StateMachine itself - public static final int CMD_START_CLIENT_MODE = 0; - public static final int CMD_START_SCAN_ONLY_MODE = 1; - public static final int CMD_DISABLE_WIFI = 3; + private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback { + private final int mMode; - private final State mWifiDisabledState = new WifiDisabledState(); - private final State mClientModeActiveState = new ClientModeActiveState(); - private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); + SoftApCallbackImpl(int mode) { + Preconditions.checkArgument(mode == WifiManager.IFACE_IP_MODE_TETHERED + || mode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + mMode = mode; + } - ModeStateMachine() { + @Override + public void onStateChanged(int state, int reason) { + if (state == WifiManager.WIFI_AP_STATE_DISABLED) { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStatsWifiState(false); + } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStatsWifiState(false); + } + switch (mMode) { + case WifiManager.IFACE_IP_MODE_TETHERED: + if (mSoftApCallback != null) mSoftApCallback.onStateChanged(state, reason); + break; + case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: + if (mLohsCallback != null) mLohsCallback.onStateChanged(state, reason); + break; + } + } + + @Override + public void onNumClientsChanged(int numClients) { + switch (mMode) { + case WifiManager.IFACE_IP_MODE_TETHERED: + if (mSoftApCallback != null) mSoftApCallback.onNumClientsChanged(numClients); + break; + case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: + if (mLohsCallback != null) mLohsCallback.onNumClientsChanged(numClients); + break; + } + } + } + + /** + * Helper method to report wifi state as on/off (doesn't matter which mode). + * + * @param enabled boolean indicating that some mode has been turned on or off + */ + private void updateBatteryStatsWifiState(boolean enabled) { + try { + if (enabled) { + if (mActiveModeManagers.size() == 1) { + // only report wifi on if we haven't already + mBatteryStats.noteWifiOn(); + } + } else { + if (mActiveModeManagers.size() == 0) { + // only report if we don't have any active modes + mBatteryStats.noteWifiOff(); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + } + + private void updateBatteryStatsScanModeActive() { + try { + mBatteryStats.noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null); + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + } + + /** + * WifiController is the class used to manage wifi state for various operating + * modes (normal, airplane, wifi hotspot, etc.). + */ + private class WifiController extends StateMachine { + private static final String TAG = "WifiController"; + + // Maximum limit to use for timeout delay if the value from overlay setting is too large. + private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; + + private final int mRecoveryDelayMillis; + + private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; + + static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; + static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; + static final int CMD_WIFI_TOGGLED = BASE + 8; + static final int CMD_AIRPLANE_TOGGLED = BASE + 9; + static final int CMD_SET_AP = BASE + 10; + static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; + static final int CMD_AP_STOPPED = BASE + 15; + static final int CMD_STA_START_FAILURE = BASE + 16; + // Command used to trigger a wifi stack restart when in active mode + static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; + // Internal command used to complete wifi stack restart + private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; + // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full + // recovery + static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; + static final int CMD_STA_STOPPED = BASE + 20; + static final int CMD_SCANNING_STOPPED = BASE + 21; + static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; + static final int CMD_SCANNING_START_FAILURE = BASE + 23; + + private final StaEnabledState mStaEnabledState = new StaEnabledState(); + private final StaDisabledState mStaDisabledState = new StaDisabledState(); + private final StaDisabledWithScanState mStaDisabledWithScanState = + new StaDisabledWithScanState(); + + WifiController() { super(TAG, mLooper); - addState(mClientModeActiveState); - addState(mScanOnlyModeActiveState); - addState(mWifiDisabledState); + DefaultState defaultState = new DefaultState(); + addState(defaultState); { + addState(mStaDisabledState, defaultState); + addState(mStaEnabledState, defaultState); + addState(mStaDisabledWithScanState, defaultState); + } + + setLogRecSize(100); + setLogOnlyTransitions(false); - Log.d(TAG, "Starting Wifi in WifiDisabledState"); - setInitialState(mWifiDisabledState); - start(); + mRecoveryDelayMillis = readWifiRecoveryDelay(); } - private String getCurrentMode() { - IState state = getCurrentState(); - if (state == null) { - return STATE_MACHINE_EXITED_STATE_NAME; + @Override + public void start() { + boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); + boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); + boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); + boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); + + log("isAirplaneModeOn = " + isAirplaneModeOn + + ", isWifiEnabled = " + isWifiEnabled + + ", isScanningAvailable = " + isScanningAlwaysAvailable + + ", isLocationModeActive = " + isLocationModeActive); + + if (checkScanOnlyModeAvailable()) { + setInitialState(mStaDisabledWithScanState); } else { - return state.getName(); + setInitialState(mStaDisabledState); } + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Location mode has been toggled... trigger with the scan change + // update to make sure we are in the correct mode + scanAlwaysModeChanged(); + } + }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); + super.start(); } - private boolean checkForAndHandleModeChange(Message message) { - switch(message.what) { - case ModeStateMachine.CMD_START_CLIENT_MODE: - Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode"); - mModeStateMachine.transitionTo(mClientModeActiveState); - break; - case ModeStateMachine.CMD_START_SCAN_ONLY_MODE: - Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode"); - mModeStateMachine.transitionTo(mScanOnlyModeActiveState); - break; - case ModeStateMachine.CMD_DISABLE_WIFI: - Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled"); - mModeStateMachine.transitionTo(mWifiDisabledState); - break; - default: - return NOT_HANDLED; + private boolean checkScanOnlyModeAvailable() { + return mWifiPermissionsUtil.isLocationModeEnabled() + && mSettingsStore.isScanAlwaysAvailable(); + } + + private int readWifiRecoveryDelay() { + int recoveryDelayMillis = mContext.getResources().getInteger( + R.integer.config_wifi_framework_recovery_timeout_delay); + if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { + recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; + Log.w(TAG, "Overriding timeout delay with maximum limit value"); } - return HANDLED; + return recoveryDelayMillis; } - class ModeActiveState extends State { - ActiveModeManager mManager; + abstract class BaseState extends State { + private boolean mIsInEmergencyCall; + private boolean mIsInEmergencyCallbackMode; + + private boolean mWasWifiDisabled; + @Override - public boolean processMessage(Message message) { - // handle messages for changing modes here - return NOT_HANDLED; + public void enter() { + super.enter(); + // Not allowed to change state when ECM is enabled! + // Thus reset to false when changing states just to be safe. + mIsInEmergencyCall = false; + mIsInEmergencyCallbackMode = false; + mWasWifiDisabled = false; + } + + @VisibleForTesting + boolean isInEmergencyMode() { + return mIsInEmergencyCall || mIsInEmergencyCallbackMode; + } + + private void updateEmergencyMode(Message msg) { + if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) { + mIsInEmergencyCall = msg.arg1 == 1; + } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) { + mIsInEmergencyCallbackMode = msg.arg1 == 1; + } + } + + private void enterEmergencyMode() { + stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); + boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext); + log("WifiController msg getConfigWiFiDisableInECBM " + configWiFiDisableInECBM); + if (configWiFiDisableInECBM) { + // TODO: this will shut down Soft AP twice in conjunction with stopSoftAPMode() + // above, is this a problem? + shutdownWifi(); + mWasWifiDisabled = true; + } + } + + private void exitEmergencyMode() { + State stateToTransitionTo; + if (mSettingsStore.isWifiToggleEnabled()) { + stateToTransitionTo = mStaEnabledState; + } else if (checkScanOnlyModeAvailable()) { + stateToTransitionTo = mStaDisabledWithScanState; + } else { + stateToTransitionTo = mStaDisabledState; + } + + if (stateToTransitionTo == this) { + // stay in same state + if (mWasWifiDisabled) { + // if Wifi was shutdown, restart the current state + this.enter(); + } + } else { + transitionTo(stateToTransitionTo); + } } @Override + public final boolean processMessage(Message msg) { + // potentially enter emergency mode + if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED + || msg.what == CMD_EMERGENCY_MODE_CHANGED) { + boolean wasInEmergencyMode = isInEmergencyMode(); + updateEmergencyMode(msg); + boolean isInEmergencyMode = isInEmergencyMode(); + if (!wasInEmergencyMode && isInEmergencyMode) { + enterEmergencyMode(); + } else if (wasInEmergencyMode && !isInEmergencyMode) { + exitEmergencyMode(); + } + return HANDLED; + } + // already in emergency mode, drop all messages + if (isInEmergencyMode()) { + return HANDLED; + } + // not in emergency mode, process messages normally + return processMessageFiltered(msg); + } + + protected abstract boolean processMessageFiltered(Message msg); + } + + class DefaultState extends State { + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_SCAN_ALWAYS_MODE_CHANGED: + case CMD_WIFI_TOGGLED: + case CMD_SCANNING_STOPPED: + case CMD_STA_STOPPED: + case CMD_STA_START_FAILURE: + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: + case CMD_DEFERRED_RECOVERY_RESTART_WIFI: + break; + case CMD_RECOVERY_DISABLE_WIFI: + log("Recovery has been throttled, disable wifi"); + shutdownWifi(); + transitionTo(mStaDisabledState); + break; + case CMD_RECOVERY_RESTART_WIFI: + deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); + shutdownWifi(); + transitionTo(mStaDisabledState); + break; + case CMD_SET_AP: + // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here + if (msg.arg1 == 1) { + enterSoftAPMode((SoftApModeConfiguration) msg.obj); + } else { + stopSoftAPMode(msg.arg2); + } + break; + case CMD_AIRPLANE_TOGGLED: + if (mSettingsStore.isAirplaneModeOn()) { + log("Airplane mode toggled, shutdown all modes"); + shutdownWifi(); + transitionTo(mStaDisabledState); + } else { + log("Airplane mode disabled, determine next state"); + if (mSettingsStore.isWifiToggleEnabled()) { + transitionTo(mStaEnabledState); + } else if (checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledWithScanState); + } + // wifi should remain disabled, do not need to transition + } + break; + case CMD_AP_STOPPED: + log("SoftAp mode disabled, determine next state"); + if (mSettingsStore.isWifiToggleEnabled()) { + transitionTo(mStaEnabledState); + } else if (checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledWithScanState); + } + // wifi should remain disabled, do not need to transition + break; + default: + throw new RuntimeException("WifiController.handleMessage " + msg.what); + } + return HANDLED; + } + } + + abstract class ModeActiveState extends BaseState { + protected ActiveModeManager mManager; + + @Override public void exit() { // Active states must have a mode manager, so this should not be null, but it isn't // obvious from the structure - add a null check here, just in case this is missed @@ -342,17 +631,18 @@ public class ActiveModeWarden { updateScanMode(); } updateBatteryStatsWifiState(false); + super.exit(); } // Hook to be used by sub-classes of ModeActiveState to indicate the completion of // bringup of the corresponding mode. - public void onModeActivationComplete() { + protected void onModeActivationComplete() { updateScanMode(); } // Update the scan state based on all active mode managers. // Note: This is an overkill currently because there is only 1 of scan-only or client - // mode present today. + // mode present today. TODO(STA+STA): multiple modes private void updateScanMode() { boolean scanEnabled = false; boolean scanningForHiddenNetworksEnabled = false; @@ -374,60 +664,88 @@ public class ActiveModeWarden { } } - class WifiDisabledState extends ModeActiveState { + class StaDisabledState extends BaseState { @Override - public void enter() { - Log.d(TAG, "Entering WifiDisabledState"); - } - - @Override - public boolean processMessage(Message message) { - if (checkForAndHandleModeChange(message)) { - return HANDLED; + public boolean processMessageFiltered(Message msg) { + switch (msg.what) { + case CMD_WIFI_TOGGLED: + if (mSettingsStore.isWifiToggleEnabled()) { + transitionTo(mStaEnabledState); + } else if (checkScanOnlyModeAvailable()) { + // only go to scan mode if we aren't in airplane mode + if (!mSettingsStore.isAirplaneModeOn()) { + transitionTo(mStaDisabledWithScanState); + } + } + break; + case CMD_SCAN_ALWAYS_MODE_CHANGED: + if (checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledWithScanState); + } + break; + case CMD_SET_AP: + if (msg.arg1 == 1) { + // remember that we were disabled, but pass the command up to start + // softap + mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); + } + return NOT_HANDLED; + case CMD_DEFERRED_RECOVERY_RESTART_WIFI: + // wait mRecoveryDelayMillis for letting driver clean reset. + sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, + mRecoveryDelayMillis); + break; + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: + if (mSettingsStore.isWifiToggleEnabled()) { + // wifi is currently disabled but the toggle is on, must have had an + // interface down before the recovery triggered + transitionTo(mStaEnabledState); + break; + } else if (checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledWithScanState); + break; + } + break; + default: + return NOT_HANDLED; } - Log.d(TAG, "Unhandled message in WifiDisabledState: " + message); - return NOT_HANDLED; - } - - @Override - public void exit() { - // do not have an active mode manager... nothing to clean up + return HANDLED; } - } - class ClientModeActiveState extends ModeActiveState { - ClientListener mListener; + class StaEnabledState extends ModeActiveState { private class ClientListener implements ClientModeManager.Listener { @Override public void onStateChanged(int state) { // make sure this listener is still active if (this != mListener) { - Log.d(TAG, "Client mode state change from previous manager"); + log("Client mode state change from previous manager"); return; } - Log.d(TAG, "State changed from client mode. state = " + state); + log("State changed from client mode. state = " + state); if (state == WifiManager.WIFI_STATE_UNKNOWN) { // error while setting up client mode or an unexpected failure. - mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this); + sendMessage(CMD_STA_START_FAILURE, this); } else if (state == WifiManager.WIFI_STATE_DISABLED) { // client mode stopped - mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this); + sendMessage(CMD_STA_STOPPED, this); } else if (state == WifiManager.WIFI_STATE_ENABLED) { // client mode is ready to go - Log.d(TAG, "client mode active"); + log("client mode active"); onModeActivationComplete(); } else { // only care if client mode stopped or started, dropping } } } + private ClientListener mListener; @Override public void enter() { - Log.d(TAG, "Entering ClientModeActiveState"); + super.enter(); + log("StaEnabledState.enter()"); mListener = new ClientListener(); mManager = mWifiInjector.makeClientModeManager(mListener); @@ -439,75 +757,122 @@ public class ActiveModeWarden { @Override public void exit() { - super.exit(); mListener = null; + super.exit(); } @Override - public boolean processMessage(Message message) { - switch(message.what) { - case CMD_START_CLIENT_MODE: - Log.d(TAG, "Received CMD_START_CLIENT_MODE when active - drop"); + public boolean processMessageFiltered(Message msg) { + switch (msg.what) { + case CMD_WIFI_TOGGLED: + if (! mSettingsStore.isWifiToggleEnabled()) { + if (checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledWithScanState); + } else { + transitionTo(mStaDisabledState); + } + } break; - case CMD_CLIENT_MODE_FAILED: - if (mListener != message.obj) { - Log.d(TAG, "Client mode state change from previous manager"); + case CMD_AIRPLANE_TOGGLED: + // airplane mode toggled on is handled in the default state + if (mSettingsStore.isAirplaneModeOn()) { + return NOT_HANDLED; + } else { + // when airplane mode is toggled off, but wifi is on, we can keep it on + log("airplane mode toggled - and airplane mode is off. return handled"); return HANDLED; } - Log.d(TAG, "ClientMode failed, return to WifiDisabledState."); - // notify WifiController that ClientMode failed - mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); - mModeStateMachine.transitionTo(mWifiDisabledState); + case CMD_STA_START_FAILURE: + if (mListener != msg.obj) { + log("StaEnabledState change from previous manager"); + break; + } + log("StaEnabledState failed, return to StaDisabled(WithScan)State."); + if (!checkScanOnlyModeAvailable()) { + transitionTo(mStaDisabledState); + } else { + transitionTo(mStaDisabledWithScanState); + } break; - case CMD_CLIENT_MODE_STOPPED: - if (mListener != message.obj) { - Log.d(TAG, "Client mode state change from previous manager"); - return HANDLED; + case CMD_SET_AP: + if (msg.arg1 == 1) { + // remember that we were enabled, but pass the command up to start + // softap + mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); } - - Log.d(TAG, "ClientMode stopped, return to WifiDisabledState."); - // notify WifiController that ClientMode stopped - mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); - mModeStateMachine.transitionTo(mWifiDisabledState); + return NOT_HANDLED; + case CMD_AP_STOPPED: + // already in a wifi mode, no need to check where we should go with softap + // stopped break; + case CMD_STA_STOPPED: + if (mListener != msg.obj) { + log("StaEnabledState change from previous manager"); + break; + } + log("StaEnabledState stopped, return to StaDisabledState."); + // Client mode stopped. Head to Disabled to wait for next command. + // We don't check whether we should go to StaDisabledWithScanState because + // the STA was stopped so that (for example) SoftAP can be turned on and the + // device doesn't support STA+AP. If we instead entered + // StaDisabledWithScanState that might kill the SoftAP that we are trying to + // start. + transitionTo(mStaDisabledState); + break; + case CMD_RECOVERY_RESTART_WIFI: + final String bugTitle; + final String bugDetail; + if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { + bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; + bugTitle = "Wi-Fi BugReport: " + bugDetail; + } else { + bugDetail = ""; + bugTitle = "Wi-Fi BugReport"; + } + if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { + mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail)); + } + // after the bug report trigger, more handling needs to be done + return NOT_HANDLED; default: - return checkForAndHandleModeChange(message); + return NOT_HANDLED; } - return NOT_HANDLED; + return HANDLED; } } - class ScanOnlyModeActiveState extends ModeActiveState { - ScanOnlyListener mListener; + class StaDisabledWithScanState extends ModeActiveState { private class ScanOnlyListener implements ScanOnlyModeManager.Listener { @Override public void onStateChanged(int state) { if (this != mListener) { - Log.d(TAG, "ScanOnly mode state change from previous manager"); + log("ScanOnly mode state change from previous manager"); return; } if (state == WifiManager.WIFI_STATE_UNKNOWN) { - Log.d(TAG, "ScanOnlyMode mode failed"); + log("StaDisabledWithScanState mode failed"); // error while setting up scan mode or an unexpected failure. - mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_FAILED, this); + sendMessage(CMD_SCANNING_START_FAILURE, this); } else if (state == WifiManager.WIFI_STATE_DISABLED) { - Log.d(TAG, "ScanOnlyMode stopped"); + log("StaDisabledWithScanState stopped"); //scan only mode stopped - mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_STOPPED, this); + sendMessage(CMD_SCANNING_STOPPED, this); } else if (state == WifiManager.WIFI_STATE_ENABLED) { // scan mode is ready to go - Log.d(TAG, "scan mode active"); + log("scan mode active"); onModeActivationComplete(); } else { - Log.d(TAG, "unexpected state update: " + state); + log("unexpected state update: " + state); } } } + private ScanOnlyListener mListener; @Override public void enter() { - Log.d(TAG, "Entering ScanOnlyModeActiveState"); + super.enter(); + log("StaDisabledWithScanState.enter()"); mListener = new ScanOnlyListener(); mManager = mWifiInjector.makeScanOnlyModeManager(mListener); @@ -520,146 +885,52 @@ public class ActiveModeWarden { @Override public void exit() { - super.exit(); mListener = null; + super.exit(); } @Override - public boolean processMessage(Message message) { - switch(message.what) { - case CMD_START_SCAN_ONLY_MODE: - Log.d(TAG, "Received CMD_START_SCAN_ONLY_MODE when active - drop"); + public boolean processMessageFiltered(Message msg) { + switch (msg.what) { + case CMD_WIFI_TOGGLED: + if (mSettingsStore.isWifiToggleEnabled()) { + transitionTo(mStaEnabledState); + } break; - case CMD_SCAN_ONLY_MODE_FAILED: - if (mListener != message.obj) { - Log.d(TAG, "ScanOnly mode state change from previous manager"); - return HANDLED; + case CMD_SCAN_ALWAYS_MODE_CHANGED: + if (!checkScanOnlyModeAvailable()) { + log("StaDisabledWithScanState: scan no longer available"); + transitionTo(mStaDisabledState); } - - Log.d(TAG, "ScanOnlyMode failed, return to WifiDisabledState."); - // notify WifiController that ScanOnlyMode failed - mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); - mModeStateMachine.transitionTo(mWifiDisabledState); break; - case CMD_SCAN_ONLY_MODE_STOPPED: - if (mListener != message.obj) { + case CMD_SET_AP: + if (msg.arg1 == 1) { + // remember that we were disabled, but pass the command up to start + // softap + mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); + } + return NOT_HANDLED; + case CMD_AP_STOPPED: + // already in a wifi mode, no need to check where we should go with softap + // stopped + break; + case CMD_SCANNING_START_FAILURE: + case CMD_SCANNING_STOPPED: + if (mListener != msg.obj) { Log.d(TAG, "ScanOnly mode state change from previous manager"); - return HANDLED; + break; } - - Log.d(TAG, "ScanOnlyMode stopped, return to WifiDisabledState."); - // notify WifiController that ScanOnlyMode stopped - mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); - mModeStateMachine.transitionTo(mWifiDisabledState); + log("StaDisabledWithScanState " + + (msg.what == CMD_SCANNING_STOPPED ? "stopped" : "failed") + + ", return to StaDisabledState."); + // stopped due to interface destruction - return to disabled and wait + transitionTo(mStaDisabledState); break; default: - return checkForAndHandleModeChange(message); + return NOT_HANDLED; } return HANDLED; } } - } // class ModeStateMachine - - private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback { - private final int mMode; - - SoftApCallbackImpl(int mode) { - Preconditions.checkArgument(mode == WifiManager.IFACE_IP_MODE_TETHERED - || mode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - mMode = mode; - } - - @Override - public void onStateChanged(int state, int reason) { - if (state == WifiManager.WIFI_AP_STATE_DISABLED) { - mActiveModeManagers.remove(getActiveModeManager()); - updateBatteryStatsWifiState(false); - } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { - mActiveModeManagers.remove(getActiveModeManager()); - updateBatteryStatsWifiState(false); - } - switch (mMode) { - case WifiManager.IFACE_IP_MODE_TETHERED: - if (mSoftApCallback != null) mSoftApCallback.onStateChanged(state, reason); - break; - case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: - if (mLohsCallback != null) mLohsCallback.onStateChanged(state, reason); - break; - } - } - - @Override - public void onNumClientsChanged(int numClients) { - switch (mMode) { - case WifiManager.IFACE_IP_MODE_TETHERED: - if (mSoftApCallback != null) mSoftApCallback.onNumClientsChanged(numClients); - break; - case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: - if (mLohsCallback != null) mLohsCallback.onNumClientsChanged(numClients); - break; - } - } - } - - private void startSoftAp(SoftApModeConfiguration softapConfig) { - Log.d(TAG, "Starting SoftApModeManager config = " - + softapConfig.getWifiConfiguration()); - - SoftApCallbackImpl callback = new SoftApCallbackImpl(softapConfig.getTargetMode()); - ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig); - callback.setActiveModeManager(manager); - manager.start(); - mActiveModeManagers.add(manager); - updateBatteryStatsWifiState(true); - } - - /** - * Helper method to report wifi state as on/off (doesn't matter which mode). - * - * @param enabled boolean indicating that some mode has been turned on or off - */ - private void updateBatteryStatsWifiState(boolean enabled) { - try { - if (enabled) { - if (mActiveModeManagers.size() == 1) { - // only report wifi on if we haven't already - mBatteryStats.noteWifiOn(); - } - } else { - if (mActiveModeManagers.size() == 0) { - // only report if we don't have any active modes - mBatteryStats.noteWifiOff(); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to note battery stats in wifi"); - } - } - - private void updateBatteryStatsScanModeActive() { - try { - mBatteryStats.noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null); - } catch (RemoteException e) { - Log.e(TAG, "Failed to note battery stats in wifi"); - } } - - // callback used to receive callbacks about underlying native failures - private final class WifiNativeStatusListener implements StatusListener { - - @Override - public void onStatusChanged(boolean isReady) { - if (!isReady) { - mHandler.post(() -> { - Log.e(TAG, "One of the native daemons died. Triggering recovery"); - mWifiDiagnostics.captureBugReportData( - WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); - - // immediately trigger SelfRecovery if we receive a notice about an - // underlying daemon failure - mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - }); - } - } - }; } diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 45dafa33f..f1165d775 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -256,7 +256,7 @@ public class ClientModeImpl extends StateMachine { } private boolean mEnableRssiPolling = false; - // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and ClientModeImpl thread. + // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and the main Wifi thread. private volatile int mPollRssiIntervalMsecs = DEFAULT_POLL_RSSI_INTERVAL_MSECS; private int mRssiPollToken = 0; /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java index 883b16a61..d2fbe1b76 100644 --- a/service/java/com/android/server/wifi/ClientModeManager.java +++ b/service/java/com/android/server/wifi/ClientModeManager.java @@ -176,7 +176,7 @@ public class ClientModeManager implements ActiveModeManager { // we must immediately clean up state in ClientModeImpl to unregister // all client mode related objects - // Note: onDestroyed is only called from the ClientModeImpl thread + // Note: onDestroyed is only called from the main Wifi thread mClientModeImpl.handleIfaceDestroyed(); sendMessage(CMD_INTERFACE_DESTROYED); diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java index c174c190e..e990f9cfb 100644 --- a/service/java/com/android/server/wifi/DppManager.java +++ b/service/java/com/android/server/wifi/DppManager.java @@ -42,7 +42,7 @@ import com.android.server.wifi.WifiNative.DppEventCallback; */ public class DppManager { private static final String TAG = "DppManager"; - public Handler mHandler; + private final Handler mHandler; private DppRequestInfo mDppRequestInfo = null; private final WifiNative mWifiNative; diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java index 97f390f39..dffee2dc2 100644 --- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java +++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java @@ -26,7 +26,7 @@ import com.android.server.wifi.nano.WifiMetricsProto; /** * This class handles the "open wi-fi network available" notification * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class OpenNetworkNotifier extends AvailableNetworkNotifier { public static final String TAG = "WifiOpenNetworkNotifier"; diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java index a4678440b..1b74191d5 100644 --- a/service/java/com/android/server/wifi/ScanRequestProxy.java +++ b/service/java/com/android/server/wifi/ScanRequestProxy.java @@ -63,7 +63,7 @@ import javax.annotation.concurrent.NotThreadSafe; * {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS}. * b) Background apps combined can request 1 scan every * {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}. - * Note: This class is not thread-safe. It needs to be invoked from ClientModeImpl thread only. + * Note: This class is not thread-safe. It needs to be invoked from the main Wifi thread only. */ @NotThreadSafe public class ScanRequestProxy { diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java index c9e95a77f..e789dd3a4 100644 --- a/service/java/com/android/server/wifi/SelfRecovery.java +++ b/service/java/com/android/server/wifi/SelfRecovery.java @@ -16,15 +16,18 @@ package com.android.server.wifi; +import android.annotation.IntDef; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Iterator; import java.util.LinkedList; /** * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism * involves triggering a stack restart (essentially simulating an airplane mode toggle) using - * {@link WifiController}. + * {@link ActiveModeWarden}. * The current triggers for: * 1. Last resort watchdog bite. * 2. HAL/wificond crashes during normal operation. @@ -39,6 +42,14 @@ public class SelfRecovery { public static final int REASON_LAST_RESORT_WATCHDOG = 0; public static final int REASON_WIFINATIVE_FAILURE = 1; public static final int REASON_STA_IFACE_DOWN = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"REASON_"}, value = { + REASON_LAST_RESORT_WATCHDOG, + REASON_WIFINATIVE_FAILURE, + REASON_STA_IFACE_DOWN}) + public @interface RecoveryReason {} + public static final long MAX_RESTARTS_IN_TIME_WINDOW = 2; // 2 restarts per hour public static final long MAX_RESTARTS_TIME_WINDOW_MILLIS = 60 * 60 * 1000; // 1 hour protected static final String[] REASON_STRINGS = { @@ -47,14 +58,14 @@ public class SelfRecovery { "Sta Interface Down" // REASON_STA_IFACE_DOWN }; - private final WifiController mWifiController; + private final ActiveModeWarden mActiveModeWarden; private final Clock mClock; // Time since boot (in millis) that restart occurred private final LinkedList<Long> mPastRestartTimes; - public SelfRecovery(WifiController wifiController, Clock clock) { - mWifiController = wifiController; + public SelfRecovery(ActiveModeWarden activeModeWarden, Clock clock) { + mActiveModeWarden = activeModeWarden; mClock = clock; - mPastRestartTimes = new LinkedList<Long>(); + mPastRestartTimes = new LinkedList<>(); } /** @@ -63,13 +74,13 @@ public class SelfRecovery { * This method does the following: * 1. Checks reason code used to trigger recovery * 2. Checks for sta iface down triggers and disables wifi by sending {@link - * WifiController#CMD_RECOVERY_DISABLE_WIFI} to {@link WifiController} to disable wifi. + * ActiveModeWarden#recoveryDisableWifi()} to {@link ActiveModeWarden} to disable wifi. * 3. Throttles restart calls for underlying native failures - * 4. Sends {@link WifiController#CMD_RECOVERY_RESTART_WIFI} to {@link WifiController} to + * 4. Sends {@link ActiveModeWarden#recoveryRestartWifi(int)} to {@link ActiveModeWarden} to * initiate the stack restart. * @param reason One of the above |REASON_*| codes. */ - public void trigger(int reason) { + public void trigger(@RecoveryReason int reason) { if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE || reason == REASON_STA_IFACE_DOWN)) { Log.e(TAG, "Invalid trigger reason. Ignoring..."); @@ -77,7 +88,7 @@ public class SelfRecovery { } if (reason == REASON_STA_IFACE_DOWN) { Log.e(TAG, "STA interface down, disable wifi"); - mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); + mActiveModeWarden.recoveryDisableWifi(); return; } @@ -88,12 +99,12 @@ public class SelfRecovery { if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) { Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in" + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Disabling wifi"); - mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); + mActiveModeWarden.recoveryDisableWifi(); return; } mPastRestartTimes.add(mClock.getElapsedSinceBootMillis()); } - mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); + mActiveModeWarden.recoveryRestartWifi(reason); } /** diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java index a0fedd577..bf36c9482 100644 --- a/service/java/com/android/server/wifi/WakeupController.java +++ b/service/java/com/android/server/wifi/WakeupController.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; - import android.content.Context; import android.database.ContentObserver; import android.net.wifi.ScanResult; @@ -403,14 +401,14 @@ public class WakeupController { /** * Enables wifi. * - * <p>This method ignores all checks and assumes that {@link WifiController} is currently + * <p>This method ignores all checks and assumes that {@link ActiveModeWarden} is currently * in ScanModeState. */ private void enableWifi() { if (USE_PLATFORM_WIFI_WAKE) { // TODO(b/72180295): ensure that there is no race condition with WifiServiceImpl here if (mWifiInjector.getWifiSettingsStore().handleWifiToggled(true /* wifiEnabled */)) { - mWifiInjector.getWifiController().sendMessage(CMD_WIFI_TOGGLED); + mWifiInjector.getActiveModeWarden().wifiToggled(); mWifiWakeMetrics.recordWakeupEvent(mNumScansHandled); } } diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 2b343fef3..227e410e5 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -176,7 +176,7 @@ public class WifiApConfigStore { * Update the current soft access point configuration. * Restore to default AP configuration if null is provided. * This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration) - * and ClientModeImpl thread (CMD_START_AP). + * and the main Wifi thread (CMD_START_AP). */ public synchronized void setApConfiguration(WifiConfiguration config) { if (config == null) { diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 71aff0592..70ae6600d 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -94,7 +94,7 @@ import java.util.Set; * in the internal database. Any configuration updates should be triggered with appropriate helper * methods of this class using the configuration's unique networkId. * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class WifiConfigManager { /** diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java index ed541a959..833c6962f 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityHelper.java +++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java @@ -29,7 +29,7 @@ import java.util.ArrayList; * access WifiNative. It starts with firmware roaming. TODO(b/34819513): Move operations * such as connection to network and legacy framework roaming here. * - * NOTE: This class is not thread safe and should only be used from the ClientModeImpl thread. + * NOTE: This class is not thread safe and should only be used from the main Wifi thread. */ public class WifiConnectivityHelper { private static final String TAG = "WifiConnectivityHelper"; diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java deleted file mode 100644 index 227d93617..000000000 --- a/service/java/com/android/server/wifi/WifiController.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wifi; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.location.LocationManager; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Log; - -import com.android.internal.R; -import com.android.internal.util.Protocol; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.server.wifi.util.WifiPermissionsUtil; - -/** - * WifiController is the class used to manage wifi state for various operating - * modes (normal, airplane, wifi hotspot, etc.). - */ -public class WifiController extends StateMachine { - private static final String TAG = "WifiController"; - private final Context mContext; - - // Maximum limit to use for timeout delay if the value from overlay setting is too large. - private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; - - /* References to values tracked in WifiService */ - private final ClientModeImpl mClientModeImpl; - private final Handler mHandler; - private final ActiveModeWarden mActiveModeWarden; - private final WifiSettingsStore mSettingsStore; - private final FrameworkFacade mFacade; - private final WifiPermissionsUtil mWifiPermissionsUtil; - - private final int mRecoveryDelayMillis; - - private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; - - static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; - static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; - static final int CMD_WIFI_TOGGLED = BASE + 8; - static final int CMD_AIRPLANE_TOGGLED = BASE + 9; - static final int CMD_SET_AP = BASE + 10; - static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; - static final int CMD_AP_STOPPED = BASE + 15; - static final int CMD_STA_START_FAILURE = BASE + 16; - // Command used to trigger a wifi stack restart when in active mode - static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; - // Internal command used to complete wifi stack restart - private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; - // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full recovery - static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; - static final int CMD_STA_STOPPED = BASE + 20; - static final int CMD_SCANNING_STOPPED = BASE + 21; - static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; - - private final DefaultState mDefaultState = new DefaultState(); - private final StaEnabledState mStaEnabledState = new StaEnabledState(); - private final StaDisabledState mStaDisabledState = new StaDisabledState(); - private final StaDisabledWithScanState mStaDisabledWithScanState = - new StaDisabledWithScanState(); - private final EcmState mEcmState = new EcmState(); - - WifiController(Context context, ClientModeImpl clientModeImpl, Looper looper, - WifiSettingsStore wss, FrameworkFacade f, - ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil) { - super(TAG, looper); - mFacade = f; - mContext = context; - mClientModeImpl = clientModeImpl; - mHandler = new Handler(looper); - mActiveModeWarden = amw; - mSettingsStore = wss; - mWifiPermissionsUtil = wifiPermissionsUtil; - - addState(mDefaultState); { - addState(mStaDisabledState, mDefaultState); - addState(mStaEnabledState, mDefaultState); - addState(mStaDisabledWithScanState, mDefaultState); - addState(mEcmState, mDefaultState); - } - - setLogRecSize(100); - setLogOnlyTransitions(false); - - // register for state updates via callbacks (vs the intents registered below) - mActiveModeWarden.registerScanOnlyCallback(state -> { - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - Log.d(TAG, "ScanOnlyMode unexpected failure: state unknown"); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - Log.d(TAG, "ScanOnlyMode stopped"); - sendMessage(CMD_SCANNING_STOPPED); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // scan mode is ready to go - Log.d(TAG, "scan mode active"); - } else { - Log.d(TAG, "unexpected state update: " + state); - } - }); - mActiveModeWarden.registerClientModeCallback(state -> { - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - logd("ClientMode unexpected failure: state unknown"); - sendMessage(CMD_STA_START_FAILURE); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - logd("ClientMode stopped"); - sendMessage(CMD_STA_STOPPED); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // scan mode is ready to go - logd("client mode active"); - } else { - logd("unexpected state update: " + state); - } - }); - - mRecoveryDelayMillis = readWifiRecoveryDelay(); - } - - @Override - public void start() { - boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); - boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); - boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); - boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); - - log("isAirplaneModeOn = " + isAirplaneModeOn - + ", isWifiEnabled = " + isWifiEnabled - + ", isScanningAvailable = " + isScanningAlwaysAvailable - + ", isLocationModeActive = " + isLocationModeActive); - - if (checkScanOnlyModeAvailable()) { - setInitialState(mStaDisabledWithScanState); - } else { - setInitialState(mStaDisabledState); - } - IntentFilter filter = new IntentFilter(); - filter.addAction(LocationManager.MODE_CHANGED_ACTION); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(LocationManager.MODE_CHANGED_ACTION)) { - // Location mode has been toggled... trigger with the scan change - // update to make sure we are in the correct mode - sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - } - } - }, - new IntentFilter(filter)); - super.start(); - } - - private boolean checkScanOnlyModeAvailable() { - return mWifiPermissionsUtil.isLocationModeEnabled() - && mSettingsStore.isScanAlwaysAvailable(); - } - - private int readWifiRecoveryDelay() { - int recoveryDelayMillis = mContext.getResources().getInteger( - R.integer.config_wifi_framework_recovery_timeout_delay); - if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { - recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; - Log.w(TAG, "Overriding timeout delay with maximum limit value"); - } - return recoveryDelayMillis; - } - - class DefaultState extends State { - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_SCAN_ALWAYS_MODE_CHANGED: - case CMD_WIFI_TOGGLED: - case CMD_SCANNING_STOPPED: - case CMD_STA_STOPPED: - case CMD_STA_START_FAILURE: - case CMD_RECOVERY_RESTART_WIFI_CONTINUE: - case CMD_DEFERRED_RECOVERY_RESTART_WIFI: - break; - case CMD_RECOVERY_DISABLE_WIFI: - log("Recovery has been throttled, disable wifi"); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - break; - case CMD_RECOVERY_RESTART_WIFI: - deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - break; - case CMD_SET_AP: - // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here - if (msg.arg1 == 1) { - mActiveModeWarden.enterSoftAPMode((SoftApModeConfiguration) msg.obj); - } else { - mActiveModeWarden.stopSoftAPMode(msg.arg2); - } - break; - case CMD_AIRPLANE_TOGGLED: - if (mSettingsStore.isAirplaneModeOn()) { - log("Airplane mode toggled, shutdown all modes"); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - } else { - log("Airplane mode disabled, determine next state"); - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } - // wifi should remain disabled, do not need to transition - } - break; - case CMD_EMERGENCY_CALL_STATE_CHANGED: - case CMD_EMERGENCY_MODE_CHANGED: - if (msg.arg1 == 1) { - transitionTo(mEcmState); - } - break; - case CMD_AP_STOPPED: - log("SoftAp mode disabled, determine next state"); - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } - // wifi should remain disabled, do not need to transition - break; - default: - throw new RuntimeException("WifiController.handleMessage " + msg.what); - } - return HANDLED; - } - } - - class StaDisabledState extends State { - @Override - public void enter() { - mActiveModeWarden.disableWifi(); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - // only go to scan mode if we aren't in airplane mode - if (mSettingsStore.isAirplaneModeOn()) { - transitionTo(mStaDisabledWithScanState); - } - } - break; - case CMD_SCAN_ALWAYS_MODE_CHANGED: - if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were disabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); - } - return NOT_HANDLED; - case CMD_DEFERRED_RECOVERY_RESTART_WIFI: - // wait mRecoveryDelayMillis for letting driver clean reset. - sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, mRecoveryDelayMillis); - break; - case CMD_RECOVERY_RESTART_WIFI_CONTINUE: - if (mSettingsStore.isWifiToggleEnabled()) { - // wifi is currently disabled but the toggle is on, must have had an - // interface down before the recovery triggered - transitionTo(mStaEnabledState); - break; - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - break; - } - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class StaEnabledState extends State { - @Override - public void enter() { - log("StaEnabledState.enter()"); - mActiveModeWarden.enterClientMode(); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (! mSettingsStore.isWifiToggleEnabled()) { - if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } else { - transitionTo(mStaDisabledState); - } - } - break; - case CMD_AIRPLANE_TOGGLED: - // airplane mode toggled on is handled in the default state - if (mSettingsStore.isAirplaneModeOn()) { - return NOT_HANDLED; - } else { - // when airplane mode is toggled off, but wifi is on, we can keep it on - log("airplane mode toggled - and airplane mode is off. return handled"); - return HANDLED; - } - case CMD_STA_START_FAILURE: - if (!checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledState); - } else { - transitionTo(mStaDisabledWithScanState); - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were enabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); - } - return NOT_HANDLED; - case CMD_AP_STOPPED: - // already in a wifi mode, no need to check where we should go with softap - // stopped - break; - case CMD_STA_STOPPED: - // Client mode stopped. head to Disabled to wait for next command - transitionTo(mStaDisabledState); - break; - case CMD_RECOVERY_RESTART_WIFI: - final String bugTitle; - final String bugDetail; - if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { - bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; - bugTitle = "Wi-Fi BugReport: " + bugDetail; - } else { - bugDetail = ""; - bugTitle = "Wi-Fi BugReport"; - } - if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { - mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail)); - } - // after the bug report trigger, more handling needs to be done - return NOT_HANDLED; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class StaDisabledWithScanState extends State { - @Override - public void enter() { - // now trigger the actual mode switch in ActiveModeWarden - mActiveModeWarden.enterScanOnlyMode(); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } - break; - case CMD_SCAN_ALWAYS_MODE_CHANGED: - if (!checkScanOnlyModeAvailable()) { - log("StaDisabledWithScanState: scan no longer available"); - transitionTo(mStaDisabledState); - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were disabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); - } - return NOT_HANDLED; - case CMD_AP_STOPPED: - // already in a wifi mode, no need to check where we should go with softap - // stopped - break; - case CMD_SCANNING_STOPPED: - // stopped due to interface destruction - return to disabled and wait - log("WifiController: SCANNING_STOPPED when in scan mode -> StaDisabled"); - transitionTo(mStaDisabledState); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class EcmState extends State { - /** - * we can enter EcmState either because an emergency call started or because - * emergency callback mode started. This count keeps track of how many such - * events happened; so we can exit after all are undone - */ - private int mEcmEntryCount; - - @Override - public void enter() { - mActiveModeWarden.stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - boolean configWiFiDisableInECBM = - mFacade.getConfigWiFiDisableInECBM(mContext); - log("WifiController msg getConfigWiFiDisableInECBM " - + configWiFiDisableInECBM); - if (configWiFiDisableInECBM) { - mActiveModeWarden.shutdownWifi(); - } - mEcmEntryCount = 1; - } - - /** - * Handles messages received while in EcmMode. - */ - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_EMERGENCY_CALL_STATE_CHANGED: - case CMD_EMERGENCY_MODE_CHANGED: - if (msg.arg1 == 1) { - mEcmEntryCount++; - } else { - mEcmEntryCount--; - } - if (mEcmEntryCount <= 0) { - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } else { - transitionTo(mStaDisabledState); - } - } - return HANDLED; - case CMD_RECOVERY_RESTART_WIFI: - case CMD_RECOVERY_DISABLE_WIFI: - // do not want to restart wifi if we are in emergency mode - return HANDLED; - case CMD_AP_STOPPED: - case CMD_SCANNING_STOPPED: - case CMD_STA_STOPPED: - // do not want to trigger a mode switch if we are in emergency mode - return HANDLED; - case CMD_SET_AP: - // do not want to start softap if we are in emergency mode - return HANDLED; - default: - return NOT_HANDLED; - } - } - } -} diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 9762db8ed..0d6c42315 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -104,7 +104,6 @@ public class WifiInjector { private OpenNetworkNotifier mOpenNetworkNotifier; private final CarrierNetworkConfig mCarrierNetworkConfig; private final WifiLockManager mLockManager; - private final WifiController mWifiController; private final WificondControl mWificondControl; private final Clock mClock = new Clock(); private final WifiMetrics mWifiMetrics; @@ -152,7 +151,7 @@ public class WifiInjector { private final LinkProbeManager mLinkProbeManager; private IpMemoryStore mIpMemoryStore; private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector; - + private final WifiThreadRunner mWifiThreadRunner; public WifiInjector(Context context) { if (context == null) { @@ -189,6 +188,7 @@ public class WifiInjector { mWifiHandlerThread.start(); Looper wifiLooper = mWifiHandlerThread.getLooper(); Handler wifiHandler = mWifiHandlerThread.getThreadHandler(); + mWifiThreadRunner = new WifiThreadRunner(wifiHandler); mWifiP2pServiceHandlerThread = new HandlerThread("WifiP2pService"); mWifiP2pServiceHandlerThread.start(); mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, wifiHandler, mFrameworkFacade); @@ -302,8 +302,9 @@ public class WifiInjector { this, mBackupManagerProxy, mCountryCode, mWifiNative, new WrongPasswordNotifier(mContext, mFrameworkFacade), mSarManager, mWifiTrafficPoller, mLinkProbeManager); - mActiveModeWarden = new ActiveModeWarden(this, mContext, wifiLooper, - mWifiNative, new DefaultModeManager(mContext), mBatteryStats); + mActiveModeWarden = new ActiveModeWarden(this, wifiLooper, + mWifiNative, new DefaultModeManager(mContext), mBatteryStats, mWifiDiagnostics, + mContext, mClientModeImpl, mSettingsStore, mFrameworkFacade, mWifiPermissionsUtil); WakeupNotificationFactory wakeupNotificationFactory = new WakeupNotificationFactory(mContext, this, mFrameworkFacade); @@ -316,9 +317,7 @@ public class WifiInjector { this, mFrameworkFacade, mClock); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService(), mClientModeImpl, mFrameworkFacade, wifiHandler, mWifiNative, mClock, mWifiMetrics); - mWifiController = new WifiController(mContext, mClientModeImpl, wifiLooper, - mSettingsStore, mFrameworkFacade, mActiveModeWarden, mWifiPermissionsUtil); - mSelfRecovery = new SelfRecovery(mWifiController, mClock); + mSelfRecovery = new SelfRecovery(mActiveModeWarden, mClock); mWifiMulticastLockManager = new WifiMulticastLockManager( mClientModeImpl.getMcastLockManagerFilterController(), BatteryStatsService.getService()); @@ -421,10 +420,6 @@ public class WifiInjector { return mClientModeImpl; } - public Handler getClientModeImplHandler() { - return mClientModeImpl.getHandler(); - } - public ActiveModeWarden getActiveModeWarden() { return mActiveModeWarden; } @@ -437,10 +432,6 @@ public class WifiInjector { return mLockManager; } - public WifiController getWifiController() { - return mWifiController; - } - public WifiLastResortWatchdog getWifiLastResortWatchdog() { return mWifiLastResortWatchdog; } @@ -588,7 +579,8 @@ public class WifiInjector { mWifiConfigManager, mWifiConfigStore, clientModeImpl, new ConnectToNetworkNotificationBuilder(mContext, this, mFrameworkFacade)); mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock, - mWifiMetrics, clientModeImpl, mWifiHandlerThread.getLooper(), mDeviceConfigFacade); + mWifiMetrics, clientModeImpl, mWifiHandlerThread.getLooper(), mDeviceConfigFacade, + mWifiThreadRunner); return new WifiConnectivityManager(mContext, getScoringParams(), clientModeImpl, this, mWifiConfigManager, clientModeImpl.getWifiInfo(), @@ -738,4 +730,8 @@ public class WifiInjector { public String getWifiStackPackageName() { return mContext.getPackageName(); } + + public WifiThreadRunner getWifiThreadRunner() { + return mWifiThreadRunner; + } } diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java index acd10572f..be45921a7 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -117,6 +117,7 @@ public class WifiLastResortWatchdog { private boolean mWatchdogFixedWifi = true; private long mLastStartConnectTime = 0; private Handler mHandler; + private final WifiThreadRunner mWifiThreadRunner; /** * Local log used for debugging any WifiLastResortWatchdog issues. @@ -125,7 +126,7 @@ public class WifiLastResortWatchdog { WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock, WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, - DeviceConfigFacade deviceConfigFacade) { + DeviceConfigFacade deviceConfigFacade, WifiThreadRunner wifiThreadRunner) { mWifiInjector = wifiInjector; mClock = clock; mWifiMetrics = wifiMetrics; @@ -133,6 +134,7 @@ public class WifiLastResortWatchdog { mClientModeImplLooper = clientModeImplLooper; mContext = context; mDeviceConfigFacade = deviceConfigFacade; + mWifiThreadRunner = wifiThreadRunner; mHandler = new Handler(clientModeImplLooper) { public void handleMessage(Message msg) { processMessage(msg); @@ -172,9 +174,8 @@ public class WifiLastResortWatchdog { + abnormalConnectionDurationMs + " milliseconds. " + "Actually took " + durationMs + " milliseconds."; logv("Triggering bug report for abnormal connection time."); - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.takeBugReport(bugTitle, bugDetail); - }); + mWifiThreadRunner.post(() -> + mClientModeImpl.takeBugReport(bugTitle, bugDetail)); } } // Should reset last connection time after each connection regardless if bugreport diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 082f56b43..e5d92356a 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -28,13 +28,6 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static android.net.wifi.WifiManager.WIFI_FEATURE_INFRA_5G; -import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED; -import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_SET_AP; -import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; - import android.annotation.CheckResult; import android.app.AppOpsManager; import android.app.admin.DeviceAdminInfo; @@ -106,7 +99,6 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.AsyncChannel; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.util.ExternalCallbackTracker; -import com.android.server.wifi.util.GeneralUtil.Mutable; import com.android.server.wifi.util.WifiHandler; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -399,12 +391,11 @@ public class WifiServiceImpl extends BaseWifiService { } private final ClientModeImplHandler mClientModeImplHandler; - private final WifiController mWifiController; private final WifiLockManager mWifiLockManager; private final WifiMulticastLockManager mWifiMulticastLockManager; private final DppManager mDppManager; - private final WifiApConfigStore mWifiApConfigStore; + private final WifiThreadRunner mWifiThreadRunner; public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) { mContext = context; @@ -430,7 +421,6 @@ public class WifiServiceImpl extends BaseWifiService { new AsyncChannelExternalClientHandler(TAG, asyncChannelHandlerThread.getLooper()); mClientModeImplHandler = new ClientModeImplHandler(TAG, asyncChannelHandlerThread.getLooper(), asyncChannel); - mWifiController = mWifiInjector.getWifiController(); mWifiBackupRestore = mWifiInjector.getWifiBackupRestore(); mWifiApConfigStore = mWifiInjector.getWifiApConfigStore(); mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil(); @@ -444,6 +434,7 @@ public class WifiServiceImpl extends BaseWifiService { mPowerProfile = mWifiInjector.getPowerProfile(); mWifiNetworkSuggestionsManager = mWifiInjector.getWifiNetworkSuggestionsManager(); mDppManager = mWifiInjector.getDppManager(); + mWifiThreadRunner = mWifiInjector.getWifiThreadRunner(); } /** @@ -475,7 +466,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onReceive(Context context, Intent intent) { if (mSettingsStore.handleAirplaneModeToggled()) { - mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED); + mActiveModeWarden.airplaneModeToggled(); } if (mSettingsStore.isAirplaneModeOn()) { Log.d(TAG, "resetting country code because Airplane mode is ON"); @@ -510,7 +501,7 @@ public class WifiServiceImpl extends BaseWifiService { if (!mClientModeImpl.syncInitialize(mClientModeImplChannel)) { Log.wtf(TAG, "Failed to initialize ClientModeImpl"); } - mWifiController.start(); + mActiveModeWarden.start(); // If we are already disabled (could be due to airplane mode), avoid changing persist // state here @@ -569,17 +560,14 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); - Mutable<Boolean> scanSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler() - .runWithScissors(() -> { - scanSuccess.value = mScanRequestProxy.startScan(callingUid, packageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to start scan"); + Boolean scanSuccess = mWifiThreadRunner.call(() -> + mScanRequestProxy.startScan(callingUid, packageName)); + if (scanSuccess == null) { + Log.e(TAG, "Timed out while synchronously starting scan"); sendFailedScanBroadcast(); return false; } - if (!scanSuccess.value) { + if (!scanSuccess) { Log.e(TAG, "Failed to start scan"); return false; } @@ -834,7 +822,7 @@ public class WifiServiceImpl extends BaseWifiService { Binder.restoreCallingIdentity(ident); } mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable); - mWifiController.sendMessage(CMD_WIFI_TOGGLED); + mActiveModeWarden.wifiToggled(); return true; } @@ -891,9 +879,7 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("updateInterfaceIpState uid=%").c(Binder.getCallingUid()).flush(); // hand off the work to our handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mLohsSoftApTracker.updateInterfaceIpState(ifaceName, mode); - }); + mWifiThreadRunner.post(() -> mLohsSoftApTracker.updateInterfaceIpState(ifaceName, mode)); } /** @@ -931,7 +917,7 @@ public class WifiServiceImpl extends BaseWifiService { // null wifiConfig is a meaningful input for CMD_SET_AP if (wifiConfig == null || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) { SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig); - mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig); + mActiveModeWarden.startSoftAp(softApConfig); return true; } Slog.e(TAG, "Invalid WifiConfiguration"); @@ -971,7 +957,7 @@ public class WifiServiceImpl extends BaseWifiService { private void stopSoftApInternal(int mode) { mLog.trace("stopSoftApInternal uid=% mode=%").c(Binder.getCallingUid()).c(mode).flush(); - mWifiController.sendMessage(CMD_SET_AP, 0, mode); + mActiveModeWarden.stopSoftAp(mode); } /** @@ -1050,7 +1036,7 @@ public class WifiServiceImpl extends BaseWifiService { } // Notify WifiController so it has a chance to turn wifi back on if (state == WIFI_AP_STATE_FAILED || state == WIFI_AP_STATE_DISABLED) { - mWifiController.sendMessage(WifiController.CMD_AP_STOPPED); + mActiveModeWarden.softApStopped(); } } @@ -1406,7 +1392,7 @@ public class WifiServiceImpl extends BaseWifiService { } // post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiThreadRunner.post(() -> { if (!mTetheredSoftApTracker.registerSoftApCallback(binder, callback, callbackIdentifier)) { Log.e(TAG, "registerSoftApCallback: Failed to add callback"); @@ -1419,7 +1405,6 @@ public class WifiServiceImpl extends BaseWifiService { } catch (RemoteException e) { Log.e(TAG, "registerSoftApCallback: remote exception -- " + e); } - }); } @@ -1438,9 +1423,8 @@ public class WifiServiceImpl extends BaseWifiService { } // post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mTetheredSoftApTracker.unregisterSoftApCallback(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mTetheredSoftApTracker.unregisterSoftApCallback(callbackIdentifier)); } /** @@ -1542,7 +1526,6 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("stopLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); mLohsSoftApTracker.stopByPid(pid); - } /** @@ -1599,15 +1582,12 @@ public class WifiServiceImpl extends BaseWifiService { // hand off work to the ClientModeImpl handler thread to sync work between calls // and SoftApManager starting up softap - final Mutable<WifiConfiguration> config = new Mutable<>(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - config.value = mWifiApConfigStore.getApConfiguration(); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (success) { - return config.value; + WifiConfiguration config = mWifiThreadRunner.call(mWifiApConfigStore::getApConfiguration); + if (config == null) { + Log.e(TAG, "Timed out while synchronously fetching AP config"); + return new WifiConfiguration(); } - Log.e(TAG, "Failed to post runnable to fetch ap config"); - return new WifiConfiguration(); + return config; } /** @@ -1632,9 +1612,7 @@ public class WifiServiceImpl extends BaseWifiService { if (wifiConfig == null) return false; if (WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) { - mClientModeImplHandler.post(() -> { - mWifiApConfigStore.setApConfiguration(wifiConfig); - }); + mWifiThreadRunner.post(() -> mWifiApConfigStore.setApConfiguration(wifiConfig)); return true; } else { Slog.e(TAG, "Invalid WifiConfiguration"); @@ -2250,12 +2228,10 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid); - final List<ScanResult> scanResults = new ArrayList<>(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - scanResults.addAll(mScanRequestProxy.getScanResults()); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!success) { - Log.e(TAG, "Failed to post runnable to fetch scan results"); + List<ScanResult> scanResults = mWifiThreadRunner.call( + mScanRequestProxy::getScanResults); + if (scanResults == null) { + Log.e(TAG, "Timed out while synchronously fetching scan results"); return new ArrayList<>(); } return scanResults; @@ -2612,11 +2588,11 @@ public class WifiServiceImpl extends BaseWifiService { } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { boolean emergencyMode = intent.getBooleanExtra(PhoneConstants.PHONE_IN_ECM_STATE, false); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0); + mActiveModeWarden.emergencyCallbackModeChanged(emergencyMode); } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) { boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0); + mActiveModeWarden.emergencyCallStateChanged(inCall); } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { handleIdleModeChanged(); } @@ -2631,7 +2607,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onChange(boolean selfChange) { mSettingsStore.handleWifiScanAlwaysAvailableToggled(); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); + mActiveModeWarden.scanAlwaysModeChanged(); } }; mFrameworkFacade.registerContentObserver(mContext, @@ -2671,8 +2647,8 @@ public class WifiServiceImpl extends BaseWifiService { String pkgName = uri.getSchemeSpecificPart(); mClientModeImpl.removeAppConfigs(pkgName, uid); - // Call the method in ClientModeImpl thread. - mWifiInjector.getClientModeImplHandler().post(() -> { + // Call the method in the main Wifi thread. + mWifiThreadRunner.post(() -> { mScanRequestProxy.clearScanRequestTimestampsForApp(pkgName, uid); // Remove all suggestions from the package. @@ -2682,7 +2658,6 @@ public class WifiServiceImpl extends BaseWifiService { // Remove all Passpoint profiles from package. mWifiInjector.getPasspointManager().removePasspointProviderWithPackage( pkgName); - }); } } @@ -2718,12 +2693,10 @@ public class WifiServiceImpl extends BaseWifiService { WifiScoreReport wifiScoreReport = mClientModeImpl.getWifiScoreReport(); if (wifiScoreReport != null) wifiScoreReport.dump(fd, pw, args); } else if (args != null && args.length > 0 && WifiScoreCard.DUMP_ARG.equals(args[0])) { - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - if (wifiScoreCard != null) { - pw.println(wifiScoreCard.getNetworkListBase64(true)); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + String networkListBase64 = mWifiThreadRunner.call(() -> + wifiScoreCard.getNetworkListBase64(true)); + pw.println(networkListBase64); } else { // Polls link layer stats and RSSI. This allows the stats to show up in // WifiScoreReport's dump() output when taking a bug report even if the screen is off. @@ -2735,7 +2708,6 @@ public class WifiServiceImpl extends BaseWifiService { Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println("mInIdleMode " + mInIdleMode); pw.println("mScanPending " + mScanPending); - mWifiController.dump(fd, pw, args); mSettingsStore.dump(fd, pw, args); mWifiTrafficPoller.dump(fd, pw, args); pw.println(); @@ -2748,35 +2720,27 @@ public class WifiServiceImpl extends BaseWifiService { pw.println(); mClientModeImpl.dump(fd, pw, args); pw.println(); - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - if (wifiScoreCard != null) { - pw.println("WifiScoreCard:"); - pw.println(wifiScoreCard.getNetworkListBase64(true)); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + String networkListBase64 = mWifiThreadRunner.call(() -> + wifiScoreCard.getNetworkListBase64(true)); + pw.println("WifiScoreCard:"); + pw.println(networkListBase64); mClientModeImpl.updateWifiMetrics(); mWifiMetrics.dump(fd, pw, args); pw.println(); - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - mWifiNetworkSuggestionsManager.dump(fd, pw, args); - pw.println(); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + mWifiThreadRunner.run(() -> mWifiNetworkSuggestionsManager.dump(fd, pw, args)); + pw.println(); mWifiBackupRestore.dump(fd, pw, args); pw.println(); pw.println("ScoringParams: settings put global " + Settings.Global.WIFI_SCORE_PARAMS + " " + mWifiInjector.getScoringParams()); pw.println(); + pw.println("WifiScoreReport:"); WifiScoreReport wifiScoreReport = mClientModeImpl.getWifiScoreReport(); - if (wifiScoreReport != null) { - pw.println("WifiScoreReport:"); - wifiScoreReport.dump(fd, pw, args); - } + wifiScoreReport.dump(fd, pw, args); pw.println(); SarManager sarManager = mWifiInjector.getSarManager(); - if (sarManager != null) { - sarManager.dump(fd, pw, args); - } + sarManager.dump(fd, pw, args); pw.println(); } } @@ -2794,18 +2758,13 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - Mutable<Boolean> lockSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - lockSuccess.value = mWifiLockManager.acquireWifiLock( - lockMode, tag, binder, updatedWs); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to acquireWifiLock"); + Boolean lockSuccess = mWifiThreadRunner.call(() -> + mWifiLockManager.acquireWifiLock(lockMode, tag, binder, updatedWs)); + if (lockSuccess == null) { + Log.e(TAG, "Timed out while synchronously calling acquireWifiLock()"); return false; } - - return lockSuccess.value; + return lockSuccess; } @Override @@ -2820,12 +2779,10 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to updateWifiLockWorkSource"); + boolean runSuccess = mWifiThreadRunner.run(() -> + mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs)); + if (!runSuccess) { + Log.e(TAG, "Timed out while synchronously calling updateWifiLockWorkSource()"); } } @@ -2835,16 +2792,14 @@ public class WifiServiceImpl extends BaseWifiService { // Check on permission to make this call mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - Mutable<Boolean> lockSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - lockSuccess.value = mWifiLockManager.releaseWifiLock(binder); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to releaseWifiLock"); + + Boolean lockSuccess = mWifiThreadRunner.call(() -> + mWifiLockManager.releaseWifiLock(binder)); + if (lockSuccess == null) { + Log.e(TAG, "Timed out while synchronously calling releaseWifiLock()"); return false; } - return lockSuccess.value; + return lockSuccess; } @Override @@ -2943,7 +2898,7 @@ public class WifiServiceImpl extends BaseWifiService { } } - mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiThreadRunner.post(() -> { mWifiInjector.getWifiConfigManager().clearDeletedEphemeralNetworks(); mClientModeImpl.clearNetworkRequestUserApprovedAccessPoints(); mWifiNetworkSuggestionsManager.clear(); @@ -3142,9 +3097,8 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("registerTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier)); } /** @@ -3162,9 +3116,8 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("unregisterTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiTrafficPoller.removeCallback(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiTrafficPoller.removeCallback(callbackIdentifier)); } private boolean is5GhzSupported() { @@ -3216,9 +3169,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.addNetworkRequestMatchCallback(binder, callback, callbackIdentifier); - }); + mWifiThreadRunner.post(() -> mClientModeImpl.addNetworkRequestMatchCallback( + binder, callback, callbackIdentifier)); } /** @@ -3237,9 +3189,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier)); } /** @@ -3260,20 +3211,17 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("addNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - Mutable<Integer> success = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - success.value = mWifiNetworkSuggestionsManager.add( - networkSuggestions, callingUid, callingPackageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to add network suggestions"); + + Integer success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.add( + networkSuggestions, callingUid, callingPackageName)); + if (success == null) { + Log.e(TAG, "Timed out while synchronously adding network suggestions"); return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; } - if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to add network suggestions"); } - return success.value; + return success; } /** @@ -3294,20 +3242,17 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("removeNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - Mutable<Integer> success = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - success.value = mWifiNetworkSuggestionsManager.remove( - networkSuggestions, callingUid, callingPackageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to remove network suggestions"); + + Integer success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.remove( + networkSuggestions, callingUid, callingPackageName)); + if (success == null) { + Log.e(TAG, "Timed out while synchronously removing network suggestions"); return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; } - if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to remove network suggestions"); } - return success.value; + return success; } /** @@ -3321,15 +3266,14 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getNetworkSuggestionList uid=%").c(Binder.getCallingUid()).flush(); } - Mutable<List<WifiNetworkSuggestion>> result = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> result.value = mWifiNetworkSuggestionsManager.get(callingPackageName), - RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to get network suggestions"); + + List<WifiNetworkSuggestion> result = mWifiThreadRunner.call(() -> + mWifiNetworkSuggestionsManager.get(callingPackageName)); + if (result == null) { + Log.e(TAG, "Timed out while synchronously getting network suggestions"); return new ArrayList<>(); } - return result.value; + return result; } /** @@ -3344,17 +3288,14 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException("App not allowed to get Wi-Fi factory MAC address " + "(uid = " + uid + ")"); } - final List<String> result = new ArrayList<>(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - final String mac = mClientModeImpl.getFactoryMacAddress(); - if (mac != null) { - result.add(mac); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (success) { - return result.isEmpty() ? null : result.toArray(new String[0]); + String result = mWifiThreadRunner.call(mClientModeImpl::getFactoryMacAddress); + // result can be null if either: WifiThreadRunner.call() timed out, or + // ClientModeImpl.getFactoryMacAddress() returned null. + // In this particular instance, we don't differentiate the two types of nulls. + if (result == null) { + return null; } - return null; + return new String[]{result}; } /** @@ -3373,8 +3314,7 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler() - .post(() -> mClientModeImpl.setDeviceMobilityState(state)); + mWifiThreadRunner.post(() -> mClientModeImpl.setDeviceMobilityState(state)); } /** @@ -3418,10 +3358,8 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mDppManager.mHandler.post(() -> { - mDppManager.startDppAsConfiguratorInitiator(uid, binder, enrolleeUri, - selectedNetworkId, netRole, callback); - }); + mWifiThreadRunner.post(() -> mDppManager.startDppAsConfiguratorInitiator( + uid, binder, enrolleeUri, selectedNetworkId, netRole, callback)); } /** @@ -3452,9 +3390,8 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mDppManager.mHandler.post(() -> { - mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback); - }); + mWifiThreadRunner.post(() -> + mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback)); } /** @@ -3467,9 +3404,7 @@ public class WifiServiceImpl extends BaseWifiService { } final int uid = getMockableCallingUid(); - mDppManager.mHandler.post(() -> { - mDppManager.stopDppSession(uid); - }); + mWifiThreadRunner.post(() -> mDppManager.stopDppSession(uid)); } /** @@ -3502,9 +3437,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier)); } /** @@ -3524,9 +3458,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier)); } /** @@ -3549,8 +3482,7 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post( - () -> mClientModeImpl.updateWifiUsabilityScore(seqNum, score, - predictionHorizonSec)); + mWifiThreadRunner.post(() -> + mClientModeImpl.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec)); } } diff --git a/service/java/com/android/server/wifi/WifiThreadRunner.java b/service/java/com/android/server/wifi/WifiThreadRunner.java new file mode 100644 index 000000000..b44b2445e --- /dev/null +++ b/service/java/com/android/server/wifi/WifiThreadRunner.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.util.Log; + +import com.android.server.wifi.util.GeneralUtil.Mutable; + +import java.util.function.Supplier; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * Runs code on the main Wifi thread from another thread, in order to prevent race conditions. + */ +@ThreadSafe +public class WifiThreadRunner { + private static final String TAG = "WifiThreadRunner"; + + /** Max wait time for posting blocking runnables */ + private static final int RUN_WITH_SCISSORS_TIMEOUT_MILLIS = 4000; + + private final Handler mHandler; + + public WifiThreadRunner(Handler handler) { + mHandler = handler; + } + + /** + * Synchronously runs code on the main Wifi thread and return a value. + * <b>Blocks</b> the calling thread until the callable completes execution on the main Wifi + * thread. + * + * BEWARE OF DEADLOCKS!!! + * + * @param <T> the return type + * @param supplier the lambda that should be run on the main Wifi thread + * e.g. wifiThreadRunner.call(() -> mWifiApConfigStore.getApConfiguration()) + * or wifiThreadRunner.call(mWifiApConfigStore::getApConfiguration) + * @return value retrieved from Wifi thread, or null if the call failed. + * Beware of NullPointerExceptions when expecting a primitive (e.g. int, long) return + * type, it may still return null and throw a NullPointerException when auto-unboxing! + * Recommend capturing the return value in an Integer or Long instead and explicitly + * handling nulls. + */ + @Nullable + public <T> T call(@NonNull Supplier<T> supplier) { + Mutable<T> result = new Mutable<>(); + boolean runWithScissorsSuccess = mHandler.runWithScissors( + () -> result.value = supplier.get(), + RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + if (runWithScissorsSuccess) { + return result.value; + } else { + Log.e(TAG, "WifiThreadRunner.call() timed out!", new Throwable("Stack trace:")); + return null; + } + } + + /** + * Runs a Runnable on the main Wifi thread and <b>blocks</b> the calling thread until the + * Runnable completes execution on the main Wifi thread. + * + * BEWARE OF DEADLOCKS!!! + * + * @return true if the runnable executed successfully, false otherwise + */ + public boolean run(@NonNull Runnable runnable) { + boolean runWithScissorsSuccess = + mHandler.runWithScissors(runnable, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + if (runWithScissorsSuccess) { + return true; + } else { + Log.e(TAG, "WifiThreadRunner.run() timed out!", new Throwable("Stack trace:")); + return false; + } + } + + /** + * Asynchronously runs a Runnable on the main Wifi thread. + * + * @return true if the runnable was successfully posted <b>(not executed)</b> to the main Wifi + * thread, false otherwise + */ + public boolean post(@NonNull Runnable runnable) { + return mHandler.post(runnable); + } +} diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index e54cee8e9..5807ba493 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -98,7 +98,7 @@ import java.util.stream.Collectors; * The provider matching requires obtaining additional information from the AP (ANQP elements). * The ANQP elements will be cached using {@link AnqpCache} to avoid unnecessary requests. * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class PasspointManager { private static final String TAG = "PasspointManager"; |