diff options
Diffstat (limited to 'com/android/server/wifi/ScanOnlyModeManager.java')
-rw-r--r-- | com/android/server/wifi/ScanOnlyModeManager.java | 228 |
1 files changed, 226 insertions, 2 deletions
diff --git a/com/android/server/wifi/ScanOnlyModeManager.java b/com/android/server/wifi/ScanOnlyModeManager.java index 1edb857f..78919e5b 100644 --- a/com/android/server/wifi/ScanOnlyModeManager.java +++ b/com/android/server/wifi/ScanOnlyModeManager.java @@ -16,27 +16,251 @@ package com.android.server.wifi; +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; +import com.android.server.wifi.WifiNative.InterfaceCallback; + /** * Manager WiFi in Scan Only Mode - no network connections. */ public class ScanOnlyModeManager implements ActiveModeManager { + private final ScanOnlyModeStateMachine mStateMachine; + private static final String TAG = "ScanOnlyModeManager"; - ScanOnlyModeManager() { + private final Context mContext; + private final WifiNative mWifiNative; + + private final WifiMetrics mWifiMetrics; + private final Listener mListener; + private final ScanRequestProxy mScanRequestProxy; + private final WakeupController mWakeupController; + + private String mClientInterfaceName; + + + ScanOnlyModeManager(Context context, @NonNull Looper looper, WifiNative wifiNative, + Listener listener, WifiMetrics wifiMetrics, ScanRequestProxy scanRequestProxy, + WakeupController wakeupController) { + mContext = context; + mWifiNative = wifiNative; + mListener = listener; + mWifiMetrics = wifiMetrics; + mScanRequestProxy = scanRequestProxy; + mWakeupController = wakeupController; + mStateMachine = new ScanOnlyModeStateMachine(looper); } /** * Start scan only mode. */ public void start() { - + mStateMachine.sendMessage(ScanOnlyModeStateMachine.CMD_START); } /** * Cancel any pending scans and stop scan mode. */ public void stop() { + mStateMachine.sendMessage(ScanOnlyModeStateMachine.CMD_STOP); + } + + /** + * Listener for ScanOnlyMode state changes. + */ + public interface Listener { + /** + * Invoke when wifi state changes. + * @param state new wifi state + */ + void onStateChanged(int state); + } + + /** + * Update Wifi state. + * @param state new Wifi state + */ + private void updateWifiState(int state) { + if (mListener != null) { + mListener.onStateChanged(state); + } + } + + private class ScanOnlyModeStateMachine extends StateMachine { + // Commands for the state machine. + public static final int CMD_START = 0; + public static final int CMD_STOP = 1; + public static final int CMD_WIFINATIVE_FAILURE = 2; + public static final int CMD_INTERFACE_STATUS_CHANGED = 3; + public static final int CMD_INTERFACE_DESTROYED = 4; + + private final State mIdleState = new IdleState(); + private final State mStartedState = new StartedState(); + + private final WifiNative.StatusListener mWifiNativeStatusListener = (boolean isReady) -> { + if (!isReady) { + sendMessage(CMD_WIFINATIVE_FAILURE); + } + }; + + private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { + @Override + public void onDestroyed(String ifaceName) { + sendMessage(CMD_INTERFACE_DESTROYED); + } + + @Override + public void onUp(String ifaceName) { + sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); + } + + @Override + public void onDown(String ifaceName) { + sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); + } + }; + private boolean mIfaceIsUp = false; + + ScanOnlyModeStateMachine(Looper looper) { + super(TAG, looper); + + addState(mIdleState); + addState(mStartedState); + + setInitialState(mIdleState); + start(); + } + + private class IdleState extends State { + + @Override + public void enter() { + Log.d(TAG, "entering IdleState"); + mWifiNative.registerStatusListener(mWifiNativeStatusListener); + mClientInterfaceName = null; + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_START: + mClientInterfaceName = mWifiNative.setupInterfaceForClientMode( + mWifiNativeInterfaceCallback); + if (TextUtils.isEmpty(mClientInterfaceName)) { + Log.e(TAG, "Failed to create ClientInterface. Sit in Idle"); + sendScanAvailableBroadcast(false); + updateWifiState(WifiManager.WIFI_STATE_UNKNOWN); + break; + } + transitionTo(mStartedState); + break; + case CMD_STOP: + // This should be safe to ignore. + Log.d(TAG, "received CMD_STOP when idle, ignoring"); + break; + default: + Log.d(TAG, "received an invalid message: " + message); + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class StartedState extends State { + + private void onUpChanged(boolean isUp) { + if (isUp == mIfaceIsUp) { + return; // no change + } + mIfaceIsUp = isUp; + if (isUp) { + Log.d(TAG, "Wifi is ready to use for scanning"); + mWakeupController.start(); + sendScanAvailableBroadcast(true); + updateWifiState(WifiManager.WIFI_STATE_ENABLED); + } else { + // if the interface goes down we should exit and go back to idle state. + Log.d(TAG, "interface down - stop scan mode"); + mStateMachine.sendMessage(CMD_STOP); + } + } + + @Override + public void enter() { + Log.d(TAG, "entering StartedState"); + mIfaceIsUp = false; + onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); + + if (mIfaceIsUp) { + // we already received the interface up notification when we were setting up + sendScanAvailableBroadcast(true); + updateWifiState(WifiManager.WIFI_STATE_ENABLED); + } + mScanRequestProxy.enableScanningForHiddenNetworks(false); + } + + @Override + public boolean processMessage(Message message) { + switch(message.what) { + case CMD_START: + // Already started, ignore this command. + break; + case CMD_STOP: + Log.d(TAG, "Stopping scan mode."); + mWifiNative.teardownInterface(mClientInterfaceName); + transitionTo(mIdleState); + break; + case CMD_INTERFACE_STATUS_CHANGED: + boolean isUp = message.arg1 == 1; + onUpChanged(isUp); + break; + case CMD_WIFINATIVE_FAILURE: + case CMD_INTERFACE_DESTROYED: + Log.d(TAG, "interface failure! restart services?"); + updateWifiState(WifiManager.WIFI_STATE_UNKNOWN); + transitionTo(mIdleState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + + /** + * Clean up state, unregister listeners and send broadcast to tell WifiScanner + * that wifi is disabled. + */ + @Override + public void exit() { + mWakeupController.stop(); + // let WifiScanner know that wifi is down. + sendScanAvailableBroadcast(false); + updateWifiState(WifiManager.WIFI_STATE_DISABLED); + mScanRequestProxy.clearScanResults(); + } + } + private void sendScanAvailableBroadcast(boolean available) { + Log.d(TAG, "sending scan available broadcast: " + available); + final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (available) { + intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_ENABLED); + } else { + intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); + } + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } } } |