diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2019-09-13 03:39:58 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-09-13 03:39:58 +0000 |
commit | 278c04302d18270c3cf8b1fde333aad8224c31b1 (patch) | |
tree | 72c71bae155bd636521fc3051132dc6978d48334 /service | |
parent | 58457bff724192c55f8348571cd4f2ddc3ff53db (diff) | |
parent | c8c5a55226be76be5d355db5c32c52df0736a249 (diff) |
Merge "Revert WifiController/ActiveModeWarden refactor due to boot time regression"
Diffstat (limited to 'service')
17 files changed, 1035 insertions, 867 deletions
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java index 4f1a00371..9a8abdf0c 100644 --- a/service/java/com/android/server/wifi/ActiveModeWarden.java +++ b/service/java/com/android/server/wifi/ActiveModeWarden.java @@ -17,11 +17,7 @@ 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; @@ -31,19 +27,16 @@ 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.util.WifiPermissionsUtil; +import com.android.server.wifi.WifiNative.StatusListener; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Collection; /** * This class provides the implementation for different WiFi operating modes. @@ -52,25 +45,64 @@ 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 final DefaultModeManager mDefaultModeManager; + private DefaultModeManager mDefaultModeManager; private final WifiInjector mWifiInjector; + private final Context mContext; private final Looper mLooper; private final Handler mHandler; - private final Context mContext; - private final ClientModeImpl mClientModeImpl; - private final WifiSettingsStore mSettingsStore; - private final FrameworkFacade mFacade; - private final WifiPermissionsUtil mWifiPermissionsUtil; + private final WifiNative mWifiNative; private final IBatteryStats mBatteryStats; + private final SelfRecovery mSelfRecovery; + private BaseWifiDiagnostics mWifiDiagnostics; private final ScanRequestProxy mScanRequestProxy; - private final WifiController mWifiController; + + // 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 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 @@ -87,107 +119,59 @@ 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, - BaseWifiDiagnostics wifiDiagnostics, - Context context, - ClientModeImpl clientModeImpl, - WifiSettingsStore settingsStore, - FrameworkFacade facade, - WifiPermissionsUtil wifiPermissionsUtil) { + IBatteryStats batteryStats) { mWifiInjector = wifiInjector; + mContext = context; mLooper = looper; mHandler = new Handler(looper); - mContext = context; - mClientModeImpl = clientModeImpl; - mSettingsStore = settingsStore; - mFacade = facade; - mWifiPermissionsUtil = wifiPermissionsUtil; + mWifiNative = wifiNative; mActiveModeManagers = new ArraySet<>(); mDefaultModeManager = defaultModeManager; mBatteryStats = batteryStats; - 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); - }); - } - }); - } - - /** 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); + mSelfRecovery = mWifiInjector.getSelfRecovery(); + mWifiDiagnostics = mWifiInjector.getWifiDiagnostics(); + mScanRequestProxy = mWifiInjector.getScanRequestProxy(); + mModeStateMachine = new ModeStateMachine(); + mWifiNativeStatusListener = new WifiNativeStatusListener(); + mWifiNative.registerStatusListener(mWifiNativeStatusListener); } /** - * Restart Wifi for recovery purposes. - * @param reason One of {@link SelfRecovery.RecoveryReason} + * Method to switch wifi into client mode where connections to configured networks will be + * attempted. */ - 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); + public void enterClientMode() { + changeMode(ModeStateMachine.CMD_START_CLIENT_MODE); } - /** 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); + /** + * 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. + */ + public void enterScanOnlyMode() { + changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE); } /** @@ -197,20 +181,9 @@ 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 */ - 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); - }); + public void enterSoftAPMode(@NonNull SoftApModeConfiguration softApConfig) { + mHandler.post(() -> startSoftAp(softApConfig)); } /** @@ -223,7 +196,7 @@ public class ActiveModeWarden { * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). * Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs. */ - private void stopSoftAPMode(int mode) { + public void stopSoftAPMode(int mode) { mHandler.post(() -> { for (ActiveModeManager manager : mActiveModeManagers) { if (!(manager instanceof SoftApManager)) continue; @@ -240,9 +213,18 @@ 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. */ - private void shutdownWifi() { + public void shutdownWifi() { mHandler.post(() -> { for (ActiveModeManager manager : mActiveModeManagers) { manager.stop(); @@ -254,33 +236,24 @@ public class ActiveModeWarden { /** * Dump current state for active mode managers. * - * Must be called from the main Wifi thread. + * Must be called from ClientModeImpl 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(); } - @VisibleForTesting - Collection<ActiveModeManager> getActiveModeManagers() { - return new ArraySet<>(mActiveModeManagers); + protected String getCurrentMode() { + return mModeStateMachine.getCurrentMode(); } - @VisibleForTesting - boolean isInEmergencyMode() { - IState state = mWifiController.getCurrentState(); - return ((WifiController.BaseState) state).isInEmergencyMode(); + private void changeMode(int newMode) { + mModeStateMachine.sendMessage(newMode); } /** @@ -298,327 +271,65 @@ public class ActiveModeWarden { } } - private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback { - private final int mMode; + 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; - SoftApCallbackImpl(int mode) { - Preconditions.checkArgument(mode == WifiManager.IFACE_IP_MODE_TETHERED - || mode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - mMode = mode; - } + private final State mWifiDisabledState = new WifiDisabledState(); + private final State mClientModeActiveState = new ClientModeActiveState(); + private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); - @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() { + ModeStateMachine() { super(TAG, mLooper); - DefaultState defaultState = new DefaultState(); - addState(defaultState); { - addState(mStaDisabledState, defaultState); - addState(mStaEnabledState, defaultState); - addState(mStaDisabledWithScanState, defaultState); - } - - setLogRecSize(100); - setLogOnlyTransitions(false); + addState(mClientModeActiveState); + addState(mScanOnlyModeActiveState); + addState(mWifiDisabledState); - mRecoveryDelayMillis = readWifiRecoveryDelay(); + Log.d(TAG, "Starting Wifi in WifiDisabledState"); + setInitialState(mWifiDisabledState); + start(); } - @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); + private String getCurrentMode() { + IState state = getCurrentState(); + if (state == null) { + return STATE_MACHINE_EXITED_STATE_NAME; } else { - 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 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 state.getName(); } - return recoveryDelayMillis; } - abstract class BaseState extends State { - private boolean mIsInEmergencyCall; - private boolean mIsInEmergencyCallbackMode; - - private boolean mWasWifiDisabled; - - @Override - 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); + 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; } - - protected abstract boolean processMessageFiltered(Message msg); + return HANDLED; } - class DefaultState extends State { + class ModeActiveState extends State { + ActiveModeManager mManager; @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; + public boolean processMessage(Message message) { + // handle messages for changing modes here + return NOT_HANDLED; } - } - - abstract class ModeActiveState extends BaseState { - protected ActiveModeManager mManager; @Override public void exit() { @@ -631,18 +342,17 @@ 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. - protected void onModeActivationComplete() { + public 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. TODO(STA+STA): multiple modes + // mode present today. private void updateScanMode() { boolean scanEnabled = false; boolean scanningForHiddenNetworksEnabled = false; @@ -664,88 +374,60 @@ public class ActiveModeWarden { } } - class StaDisabledState extends BaseState { + class WifiDisabledState extends ModeActiveState { @Override - 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; + public void enter() { + Log.d(TAG, "Entering WifiDisabledState"); + } + + @Override + public boolean processMessage(Message message) { + if (checkForAndHandleModeChange(message)) { + return HANDLED; } - return 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 } + } - class StaEnabledState extends ModeActiveState { + class ClientModeActiveState extends ModeActiveState { + ClientListener mListener; private class ClientListener implements ClientModeManager.Listener { @Override public void onStateChanged(int state) { // make sure this listener is still active if (this != mListener) { - log("Client mode state change from previous manager"); + Log.d(TAG, "Client mode state change from previous manager"); return; } - log("State changed from client mode. state = " + state); + Log.d(TAG, "State changed from client mode. state = " + state); if (state == WifiManager.WIFI_STATE_UNKNOWN) { // error while setting up client mode or an unexpected failure. - sendMessage(CMD_STA_START_FAILURE, this); + mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this); } else if (state == WifiManager.WIFI_STATE_DISABLED) { // client mode stopped - sendMessage(CMD_STA_STOPPED, this); + mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this); } else if (state == WifiManager.WIFI_STATE_ENABLED) { // client mode is ready to go - log("client mode active"); + Log.d(TAG, "client mode active"); onModeActivationComplete(); } else { // only care if client mode stopped or started, dropping } } } - private ClientListener mListener; @Override public void enter() { - super.enter(); - log("StaEnabledState.enter()"); + Log.d(TAG, "Entering ClientModeActiveState"); mListener = new ClientListener(); mManager = mWifiInjector.makeClientModeManager(mListener); @@ -757,122 +439,75 @@ public class ActiveModeWarden { @Override public void exit() { - mListener = null; super.exit(); + mListener = null; } @Override - public boolean processMessageFiltered(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (! mSettingsStore.isWifiToggleEnabled()) { - if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } else { - transitionTo(mStaDisabledState); - } - } + public boolean processMessage(Message message) { + switch(message.what) { + case CMD_START_CLIENT_MODE: + Log.d(TAG, "Received CMD_START_CLIENT_MODE when active - drop"); 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"); + case CMD_CLIENT_MODE_FAILED: + if (mListener != message.obj) { + Log.d(TAG, "Client mode state change from previous manager"); return HANDLED; } - 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_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 + Log.d(TAG, "ClientMode failed, return to WifiDisabledState."); + // notify WifiController that ClientMode failed + mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); + mModeStateMachine.transitionTo(mWifiDisabledState); break; - case CMD_STA_STOPPED: - if (mListener != msg.obj) { - log("StaEnabledState change from previous manager"); - break; + case CMD_CLIENT_MODE_STOPPED: + if (mListener != message.obj) { + Log.d(TAG, "Client mode state change from previous manager"); + return HANDLED; } - 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); + + Log.d(TAG, "ClientMode stopped, return to WifiDisabledState."); + // notify WifiController that ClientMode stopped + mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); + mModeStateMachine.transitionTo(mWifiDisabledState); 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 checkForAndHandleModeChange(message); } - return HANDLED; + return NOT_HANDLED; } } - class StaDisabledWithScanState extends ModeActiveState { + class ScanOnlyModeActiveState extends ModeActiveState { + ScanOnlyListener mListener; private class ScanOnlyListener implements ScanOnlyModeManager.Listener { @Override public void onStateChanged(int state) { if (this != mListener) { - log("ScanOnly mode state change from previous manager"); + Log.d(TAG, "ScanOnly mode state change from previous manager"); return; } if (state == WifiManager.WIFI_STATE_UNKNOWN) { - log("StaDisabledWithScanState mode failed"); + Log.d(TAG, "ScanOnlyMode mode failed"); // error while setting up scan mode or an unexpected failure. - sendMessage(CMD_SCANNING_START_FAILURE, this); + mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_FAILED, this); } else if (state == WifiManager.WIFI_STATE_DISABLED) { - log("StaDisabledWithScanState stopped"); + Log.d(TAG, "ScanOnlyMode stopped"); //scan only mode stopped - sendMessage(CMD_SCANNING_STOPPED, this); + mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_STOPPED, this); } else if (state == WifiManager.WIFI_STATE_ENABLED) { // scan mode is ready to go - log("scan mode active"); + Log.d(TAG, "scan mode active"); onModeActivationComplete(); } else { - log("unexpected state update: " + state); + Log.d(TAG, "unexpected state update: " + state); } } } - private ScanOnlyListener mListener; @Override public void enter() { - super.enter(); - log("StaDisabledWithScanState.enter()"); + Log.d(TAG, "Entering ScanOnlyModeActiveState"); mListener = new ScanOnlyListener(); mManager = mWifiInjector.makeScanOnlyModeManager(mListener); @@ -885,52 +520,146 @@ public class ActiveModeWarden { @Override public void exit() { - mListener = null; super.exit(); + mListener = null; } @Override - public boolean processMessageFiltered(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); - } + 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"); 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); + case CMD_SCAN_ONLY_MODE_FAILED: + if (mListener != message.obj) { + Log.d(TAG, "ScanOnly mode state change from previous manager"); + return HANDLED; } - return NOT_HANDLED; - case CMD_AP_STOPPED: - // already in a wifi mode, no need to check where we should go with softap - // stopped + + 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_SCANNING_START_FAILURE: - case CMD_SCANNING_STOPPED: - if (mListener != msg.obj) { + case CMD_SCAN_ONLY_MODE_STOPPED: + if (mListener != message.obj) { Log.d(TAG, "ScanOnly mode state change from previous manager"); - break; + return HANDLED; } - log("StaDisabledWithScanState " - + (msg.what == CMD_SCANNING_STOPPED ? "stopped" : "failed") - + ", return to StaDisabledState."); - // stopped due to interface destruction - return to disabled and wait - transitionTo(mStaDisabledState); + + Log.d(TAG, "ScanOnlyMode stopped, return to WifiDisabledState."); + // notify WifiController that ScanOnlyMode stopped + mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); + mModeStateMachine.transitionTo(mWifiDisabledState); break; default: - return NOT_HANDLED; + return checkForAndHandleModeChange(message); } 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 4559cf0b1..9100ad9f7 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 the main Wifi thread. + // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and ClientModeImpl 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 d2fbe1b76..883b16a61 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 main Wifi thread + // Note: onDestroyed is only called from the ClientModeImpl 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 e990f9cfb..c174c190e 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"; - private final Handler mHandler; + public 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 dffee2dc2..97f390f39 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 the main Wifi thread. + * NOTE: These API's are not thread safe and should only be used from ClientModeImpl 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 1b74191d5..a4678440b 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 the main Wifi thread only. + * Note: This class is not thread-safe. It needs to be invoked from ClientModeImpl 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 e789dd3a4..c9e95a77f 100644 --- a/service/java/com/android/server/wifi/SelfRecovery.java +++ b/service/java/com/android/server/wifi/SelfRecovery.java @@ -16,18 +16,15 @@ 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 ActiveModeWarden}. + * {@link WifiController}. * The current triggers for: * 1. Last resort watchdog bite. * 2. HAL/wificond crashes during normal operation. @@ -42,14 +39,6 @@ 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 = { @@ -58,14 +47,14 @@ public class SelfRecovery { "Sta Interface Down" // REASON_STA_IFACE_DOWN }; - private final ActiveModeWarden mActiveModeWarden; + private final WifiController mWifiController; private final Clock mClock; // Time since boot (in millis) that restart occurred private final LinkedList<Long> mPastRestartTimes; - public SelfRecovery(ActiveModeWarden activeModeWarden, Clock clock) { - mActiveModeWarden = activeModeWarden; + public SelfRecovery(WifiController wifiController, Clock clock) { + mWifiController = wifiController; mClock = clock; - mPastRestartTimes = new LinkedList<>(); + mPastRestartTimes = new LinkedList<Long>(); } /** @@ -74,13 +63,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 - * ActiveModeWarden#recoveryDisableWifi()} to {@link ActiveModeWarden} to disable wifi. + * WifiController#CMD_RECOVERY_DISABLE_WIFI} to {@link WifiController} to disable wifi. * 3. Throttles restart calls for underlying native failures - * 4. Sends {@link ActiveModeWarden#recoveryRestartWifi(int)} to {@link ActiveModeWarden} to + * 4. Sends {@link WifiController#CMD_RECOVERY_RESTART_WIFI} to {@link WifiController} to * initiate the stack restart. * @param reason One of the above |REASON_*| codes. */ - public void trigger(@RecoveryReason int reason) { + public void trigger(int reason) { if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE || reason == REASON_STA_IFACE_DOWN)) { Log.e(TAG, "Invalid trigger reason. Ignoring..."); @@ -88,7 +77,7 @@ public class SelfRecovery { } if (reason == REASON_STA_IFACE_DOWN) { Log.e(TAG, "STA interface down, disable wifi"); - mActiveModeWarden.recoveryDisableWifi(); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); return; } @@ -99,12 +88,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"); - mActiveModeWarden.recoveryDisableWifi(); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); return; } mPastRestartTimes.add(mClock.getElapsedSinceBootMillis()); } - mActiveModeWarden.recoveryRestartWifi(reason); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); } /** diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java index bf36c9482..a0fedd577 100644 --- a/service/java/com/android/server/wifi/WakeupController.java +++ b/service/java/com/android/server/wifi/WakeupController.java @@ -16,6 +16,8 @@ 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; @@ -401,14 +403,14 @@ public class WakeupController { /** * Enables wifi. * - * <p>This method ignores all checks and assumes that {@link ActiveModeWarden} is currently + * <p>This method ignores all checks and assumes that {@link WifiController} 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.getActiveModeWarden().wifiToggled(); + mWifiInjector.getWifiController().sendMessage(CMD_WIFI_TOGGLED); mWifiWakeMetrics.recordWakeupEvent(mNumScansHandled); } } diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 227e410e5..2b343fef3 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 the main Wifi thread (CMD_START_AP). + * and ClientModeImpl 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 70ae6600d..71aff0592 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 the main Wifi thread. + * NOTE: These API's are not thread safe and should only be used from ClientModeImpl 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 833c6962f..ed541a959 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 main Wifi thread. + * NOTE: This class is not thread safe and should only be used from the ClientModeImpl 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 new file mode 100644 index 000000000..227d93617 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiController.java @@ -0,0 +1,482 @@ +/* + * 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 2a2c28da9..722b1532d 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -105,6 +105,7 @@ 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 +153,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,7 +190,6 @@ public class WifiInjector { mWifiHandlerThread.start(); Looper wifiLooper = mWifiHandlerThread.getLooper(); Handler wifiHandler = mWifiHandlerThread.getThreadHandler(); - mWifiThreadRunner = new WifiThreadRunner(wifiHandler); mWifiP2pServiceHandlerThread = new HandlerThread("WifiP2pService"); mWifiP2pServiceHandlerThread.start(); mPasspointProvisionerHandlerThread = @@ -306,9 +306,8 @@ public class WifiInjector { this, mBackupManagerProxy, mCountryCode, mWifiNative, new WrongPasswordNotifier(mContext, mFrameworkFacade), mSarManager, mWifiTrafficPoller, mLinkProbeManager); - mActiveModeWarden = new ActiveModeWarden(this, wifiLooper, - mWifiNative, new DefaultModeManager(mContext), mBatteryStats, mWifiDiagnostics, - mContext, mClientModeImpl, mSettingsStore, mFrameworkFacade, mWifiPermissionsUtil); + mActiveModeWarden = new ActiveModeWarden(this, mContext, wifiLooper, + mWifiNative, new DefaultModeManager(mContext), mBatteryStats); WakeupNotificationFactory wakeupNotificationFactory = new WakeupNotificationFactory(mContext, this, mFrameworkFacade); @@ -321,7 +320,9 @@ public class WifiInjector { this, mFrameworkFacade, mClock); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService(), mClientModeImpl, mFrameworkFacade, wifiHandler, mWifiNative, mClock, mWifiMetrics); - mSelfRecovery = new SelfRecovery(mActiveModeWarden, mClock); + mWifiController = new WifiController(mContext, mClientModeImpl, wifiLooper, + mSettingsStore, mFrameworkFacade, mActiveModeWarden, mWifiPermissionsUtil); + mSelfRecovery = new SelfRecovery(mWifiController, mClock); mWifiMulticastLockManager = new WifiMulticastLockManager( mClientModeImpl.getMcastLockManagerFilterController(), BatteryStatsService.getService()); @@ -428,6 +429,10 @@ public class WifiInjector { return mClientModeImpl; } + public Handler getClientModeImplHandler() { + return mClientModeImpl.getHandler(); + } + public ActiveModeWarden getActiveModeWarden() { return mActiveModeWarden; } @@ -440,6 +445,10 @@ public class WifiInjector { return mLockManager; } + public WifiController getWifiController() { + return mWifiController; + } + public WifiLastResortWatchdog getWifiLastResortWatchdog() { return mWifiLastResortWatchdog; } @@ -587,8 +596,7 @@ public class WifiInjector { mWifiConfigManager, mWifiConfigStore, clientModeImpl, new ConnectToNetworkNotificationBuilder(mContext, this, mFrameworkFacade)); mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock, - mWifiMetrics, clientModeImpl, mWifiHandlerThread.getLooper(), mDeviceConfigFacade, - mWifiThreadRunner); + mWifiMetrics, clientModeImpl, mWifiHandlerThread.getLooper(), mDeviceConfigFacade); return new WifiConnectivityManager(mContext, getScoringParams(), clientModeImpl, this, mWifiConfigManager, clientModeImpl.getWifiInfo(), @@ -738,8 +746,4 @@ 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 be45921a7..acd10572f 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -117,7 +117,6 @@ 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. @@ -126,7 +125,7 @@ public class WifiLastResortWatchdog { WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock, WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, - DeviceConfigFacade deviceConfigFacade, WifiThreadRunner wifiThreadRunner) { + DeviceConfigFacade deviceConfigFacade) { mWifiInjector = wifiInjector; mClock = clock; mWifiMetrics = wifiMetrics; @@ -134,7 +133,6 @@ public class WifiLastResortWatchdog { mClientModeImplLooper = clientModeImplLooper; mContext = context; mDeviceConfigFacade = deviceConfigFacade; - mWifiThreadRunner = wifiThreadRunner; mHandler = new Handler(clientModeImplLooper) { public void handleMessage(Message msg) { processMessage(msg); @@ -174,8 +172,9 @@ public class WifiLastResortWatchdog { + abnormalConnectionDurationMs + " milliseconds. " + "Actually took " + durationMs + " milliseconds."; logv("Triggering bug report for abnormal connection time."); - mWifiThreadRunner.post(() -> - mClientModeImpl.takeBugReport(bugTitle, bugDetail)); + mWifiInjector.getClientModeImplHandler().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 e5d92356a..082f56b43 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -28,6 +28,13 @@ 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; @@ -99,6 +106,7 @@ 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; @@ -391,11 +399,12 @@ 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; @@ -421,6 +430,7 @@ 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(); @@ -434,7 +444,6 @@ public class WifiServiceImpl extends BaseWifiService { mPowerProfile = mWifiInjector.getPowerProfile(); mWifiNetworkSuggestionsManager = mWifiInjector.getWifiNetworkSuggestionsManager(); mDppManager = mWifiInjector.getDppManager(); - mWifiThreadRunner = mWifiInjector.getWifiThreadRunner(); } /** @@ -466,7 +475,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onReceive(Context context, Intent intent) { if (mSettingsStore.handleAirplaneModeToggled()) { - mActiveModeWarden.airplaneModeToggled(); + mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED); } if (mSettingsStore.isAirplaneModeOn()) { Log.d(TAG, "resetting country code because Airplane mode is ON"); @@ -501,7 +510,7 @@ public class WifiServiceImpl extends BaseWifiService { if (!mClientModeImpl.syncInitialize(mClientModeImplChannel)) { Log.wtf(TAG, "Failed to initialize ClientModeImpl"); } - mActiveModeWarden.start(); + mWifiController.start(); // If we are already disabled (could be due to airplane mode), avoid changing persist // state here @@ -560,14 +569,17 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); - Boolean scanSuccess = mWifiThreadRunner.call(() -> - mScanRequestProxy.startScan(callingUid, packageName)); - if (scanSuccess == null) { - Log.e(TAG, "Timed out while synchronously starting scan"); + 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"); sendFailedScanBroadcast(); return false; } - if (!scanSuccess) { + if (!scanSuccess.value) { Log.e(TAG, "Failed to start scan"); return false; } @@ -822,7 +834,7 @@ public class WifiServiceImpl extends BaseWifiService { Binder.restoreCallingIdentity(ident); } mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable); - mActiveModeWarden.wifiToggled(); + mWifiController.sendMessage(CMD_WIFI_TOGGLED); return true; } @@ -879,7 +891,9 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("updateInterfaceIpState uid=%").c(Binder.getCallingUid()).flush(); // hand off the work to our handler thread - mWifiThreadRunner.post(() -> mLohsSoftApTracker.updateInterfaceIpState(ifaceName, mode)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mLohsSoftApTracker.updateInterfaceIpState(ifaceName, mode); + }); } /** @@ -917,7 +931,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); - mActiveModeWarden.startSoftAp(softApConfig); + mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig); return true; } Slog.e(TAG, "Invalid WifiConfiguration"); @@ -957,7 +971,7 @@ public class WifiServiceImpl extends BaseWifiService { private void stopSoftApInternal(int mode) { mLog.trace("stopSoftApInternal uid=% mode=%").c(Binder.getCallingUid()).c(mode).flush(); - mActiveModeWarden.stopSoftAp(mode); + mWifiController.sendMessage(CMD_SET_AP, 0, mode); } /** @@ -1036,7 +1050,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) { - mActiveModeWarden.softApStopped(); + mWifiController.sendMessage(WifiController.CMD_AP_STOPPED); } } @@ -1392,7 +1406,7 @@ public class WifiServiceImpl extends BaseWifiService { } // post operation to handler thread - mWifiThreadRunner.post(() -> { + mWifiInjector.getClientModeImplHandler().post(() -> { if (!mTetheredSoftApTracker.registerSoftApCallback(binder, callback, callbackIdentifier)) { Log.e(TAG, "registerSoftApCallback: Failed to add callback"); @@ -1405,6 +1419,7 @@ public class WifiServiceImpl extends BaseWifiService { } catch (RemoteException e) { Log.e(TAG, "registerSoftApCallback: remote exception -- " + e); } + }); } @@ -1423,8 +1438,9 @@ public class WifiServiceImpl extends BaseWifiService { } // post operation to handler thread - mWifiThreadRunner.post(() -> - mTetheredSoftApTracker.unregisterSoftApCallback(callbackIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mTetheredSoftApTracker.unregisterSoftApCallback(callbackIdentifier); + }); } /** @@ -1526,6 +1542,7 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("stopLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); mLohsSoftApTracker.stopByPid(pid); + } /** @@ -1582,12 +1599,15 @@ public class WifiServiceImpl extends BaseWifiService { // hand off work to the ClientModeImpl handler thread to sync work between calls // and SoftApManager starting up softap - WifiConfiguration config = mWifiThreadRunner.call(mWifiApConfigStore::getApConfiguration); - if (config == null) { - Log.e(TAG, "Timed out while synchronously fetching AP config"); - return new WifiConfiguration(); + 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; } - return config; + Log.e(TAG, "Failed to post runnable to fetch ap config"); + return new WifiConfiguration(); } /** @@ -1612,7 +1632,9 @@ public class WifiServiceImpl extends BaseWifiService { if (wifiConfig == null) return false; if (WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) { - mWifiThreadRunner.post(() -> mWifiApConfigStore.setApConfiguration(wifiConfig)); + mClientModeImplHandler.post(() -> { + mWifiApConfigStore.setApConfiguration(wifiConfig); + }); return true; } else { Slog.e(TAG, "Invalid WifiConfiguration"); @@ -2228,10 +2250,12 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid); - List<ScanResult> scanResults = mWifiThreadRunner.call( - mScanRequestProxy::getScanResults); - if (scanResults == null) { - Log.e(TAG, "Timed out while synchronously fetching scan results"); + 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"); return new ArrayList<>(); } return scanResults; @@ -2588,11 +2612,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); - mActiveModeWarden.emergencyCallbackModeChanged(emergencyMode); + mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0); } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) { boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false); - mActiveModeWarden.emergencyCallStateChanged(inCall); + mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0); } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { handleIdleModeChanged(); } @@ -2607,7 +2631,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onChange(boolean selfChange) { mSettingsStore.handleWifiScanAlwaysAvailableToggled(); - mActiveModeWarden.scanAlwaysModeChanged(); + mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); } }; mFrameworkFacade.registerContentObserver(mContext, @@ -2647,8 +2671,8 @@ public class WifiServiceImpl extends BaseWifiService { String pkgName = uri.getSchemeSpecificPart(); mClientModeImpl.removeAppConfigs(pkgName, uid); - // Call the method in the main Wifi thread. - mWifiThreadRunner.post(() -> { + // Call the method in ClientModeImpl thread. + mWifiInjector.getClientModeImplHandler().post(() -> { mScanRequestProxy.clearScanRequestTimestampsForApp(pkgName, uid); // Remove all suggestions from the package. @@ -2658,6 +2682,7 @@ public class WifiServiceImpl extends BaseWifiService { // Remove all Passpoint profiles from package. mWifiInjector.getPasspointManager().removePasspointProviderWithPackage( pkgName); + }); } } @@ -2693,10 +2718,12 @@ 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])) { - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - String networkListBase64 = mWifiThreadRunner.call(() -> - wifiScoreCard.getNetworkListBase64(true)); - pw.println(networkListBase64); + mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + if (wifiScoreCard != null) { + pw.println(wifiScoreCard.getNetworkListBase64(true)); + } + }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); } 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. @@ -2708,6 +2735,7 @@ 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(); @@ -2720,27 +2748,35 @@ public class WifiServiceImpl extends BaseWifiService { pw.println(); mClientModeImpl.dump(fd, pw, args); pw.println(); - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - String networkListBase64 = mWifiThreadRunner.call(() -> - wifiScoreCard.getNetworkListBase64(true)); - pw.println("WifiScoreCard:"); - pw.println(networkListBase64); + mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + if (wifiScoreCard != null) { + pw.println("WifiScoreCard:"); + pw.println(wifiScoreCard.getNetworkListBase64(true)); + } + }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); mClientModeImpl.updateWifiMetrics(); mWifiMetrics.dump(fd, pw, args); pw.println(); - mWifiThreadRunner.run(() -> mWifiNetworkSuggestionsManager.dump(fd, pw, args)); - pw.println(); + mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { + mWifiNetworkSuggestionsManager.dump(fd, pw, args); + pw.println(); + }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); 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(); - wifiScoreReport.dump(fd, pw, args); + if (wifiScoreReport != null) { + pw.println("WifiScoreReport:"); + wifiScoreReport.dump(fd, pw, args); + } pw.println(); SarManager sarManager = mWifiInjector.getSarManager(); - sarManager.dump(fd, pw, args); + if (sarManager != null) { + sarManager.dump(fd, pw, args); + } pw.println(); } } @@ -2758,13 +2794,18 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - Boolean lockSuccess = mWifiThreadRunner.call(() -> - mWifiLockManager.acquireWifiLock(lockMode, tag, binder, updatedWs)); - if (lockSuccess == null) { - Log.e(TAG, "Timed out while synchronously calling acquireWifiLock()"); + 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"); return false; } - return lockSuccess; + + return lockSuccess.value; } @Override @@ -2779,10 +2820,12 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - boolean runSuccess = mWifiThreadRunner.run(() -> - mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs)); - if (!runSuccess) { - Log.e(TAG, "Timed out while synchronously calling updateWifiLockWorkSource()"); + 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"); } } @@ -2792,14 +2835,16 @@ public class WifiServiceImpl extends BaseWifiService { // Check on permission to make this call mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - - Boolean lockSuccess = mWifiThreadRunner.call(() -> - mWifiLockManager.releaseWifiLock(binder)); - if (lockSuccess == null) { - Log.e(TAG, "Timed out while synchronously calling releaseWifiLock()"); + 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"); return false; } - return lockSuccess; + return lockSuccess.value; } @Override @@ -2898,7 +2943,7 @@ public class WifiServiceImpl extends BaseWifiService { } } - mWifiThreadRunner.post(() -> { + mWifiInjector.getClientModeImplHandler().post(() -> { mWifiInjector.getWifiConfigManager().clearDeletedEphemeralNetworks(); mClientModeImpl.clearNetworkRequestUserApprovedAccessPoints(); mWifiNetworkSuggestionsManager.clear(); @@ -3097,8 +3142,9 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("registerTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier); + }); } /** @@ -3116,8 +3162,9 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("unregisterTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mWifiTrafficPoller.removeCallback(callbackIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiTrafficPoller.removeCallback(callbackIdentifier); + }); } private boolean is5GhzSupported() { @@ -3169,8 +3216,9 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> mClientModeImpl.addNetworkRequestMatchCallback( - binder, callback, callbackIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mClientModeImpl.addNetworkRequestMatchCallback(binder, callback, callbackIdentifier); + }); } /** @@ -3189,8 +3237,9 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier); + }); } /** @@ -3211,17 +3260,20 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("addNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - - Integer success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.add( - networkSuggestions, callingUid, callingPackageName)); - if (success == null) { - Log.e(TAG, "Timed out while synchronously adding network suggestions"); + 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"); return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; } - if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to add network suggestions"); } - return success; + return success.value; } /** @@ -3242,17 +3294,20 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("removeNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - - Integer success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.remove( - networkSuggestions, callingUid, callingPackageName)); - if (success == null) { - Log.e(TAG, "Timed out while synchronously removing network suggestions"); + 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"); return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; } - if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to remove network suggestions"); } - return success; + return success.value; } /** @@ -3266,14 +3321,15 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getNetworkSuggestionList uid=%").c(Binder.getCallingUid()).flush(); } - - List<WifiNetworkSuggestion> result = mWifiThreadRunner.call(() -> - mWifiNetworkSuggestionsManager.get(callingPackageName)); - if (result == null) { - Log.e(TAG, "Timed out while synchronously getting network suggestions"); + 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"); return new ArrayList<>(); } - return result; + return result.value; } /** @@ -3288,14 +3344,17 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException("App not allowed to get Wi-Fi factory MAC address " + "(uid = " + uid + ")"); } - 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; + 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]); } - return new String[]{result}; + return null; } /** @@ -3314,7 +3373,8 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> mClientModeImpl.setDeviceMobilityState(state)); + mWifiInjector.getClientModeImplHandler() + .post(() -> mClientModeImpl.setDeviceMobilityState(state)); } /** @@ -3358,8 +3418,10 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mWifiThreadRunner.post(() -> mDppManager.startDppAsConfiguratorInitiator( - uid, binder, enrolleeUri, selectedNetworkId, netRole, callback)); + mDppManager.mHandler.post(() -> { + mDppManager.startDppAsConfiguratorInitiator(uid, binder, enrolleeUri, + selectedNetworkId, netRole, callback); + }); } /** @@ -3390,8 +3452,9 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mWifiThreadRunner.post(() -> - mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback)); + mDppManager.mHandler.post(() -> { + mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback); + }); } /** @@ -3404,7 +3467,9 @@ public class WifiServiceImpl extends BaseWifiService { } final int uid = getMockableCallingUid(); - mWifiThreadRunner.post(() -> mDppManager.stopDppSession(uid)); + mDppManager.mHandler.post(() -> { + mDppManager.stopDppSession(uid); + }); } /** @@ -3437,8 +3502,9 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier); + }); } /** @@ -3458,8 +3524,9 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier)); + mWifiInjector.getClientModeImplHandler().post(() -> { + mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier); + }); } /** @@ -3482,7 +3549,8 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiThreadRunner.post(() -> - mClientModeImpl.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec)); + mWifiInjector.getClientModeImplHandler().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 deleted file mode 100644 index b44b2445e..000000000 --- a/service/java/com/android/server/wifi/WifiThreadRunner.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 9e0a26fab..21019a8c9 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 the main Wifi thread. + * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. */ public class PasspointManager { private static final String TAG = "PasspointManager"; |