diff options
author | Rebecca Silberstein <silberst@google.com> | 2018-01-24 02:27:42 -0800 |
---|---|---|
committer | Rebecca Silberstein <silberst@google.com> | 2018-03-09 02:59:49 -0800 |
commit | e845cb0859529bf544208dc5d5711535231a04c0 (patch) | |
tree | b04679105560d34f4d833b7ad97d0f0ec111004e /service | |
parent | 34226fdc35f33a5c3a95e0b86138c933c5682370 (diff) |
ClientModeManager: add interface control
Add interface control using the new WifiNative APIs to
ClientModeManager. Unit tests are also added to cover interface
creation and teardown events.
Bug: 32941234
Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh
Change-Id: I367c649be5e5c5b7e0925583e9479cef72ed5c77
Diffstat (limited to 'service')
3 files changed, 287 insertions, 4 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java index 7ab33ddb9..14d55de1e 100644 --- a/service/java/com/android/server/wifi/ClientModeManager.java +++ b/service/java/com/android/server/wifi/ClientModeManager.java @@ -16,27 +16,268 @@ 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 Client Mode where we connect to configured networks. */ public class ClientModeManager implements ActiveModeManager { + private static final String TAG = "WifiClientModeManager"; + + private final ClientModeStateMachine mStateMachine; + + private final Context mContext; + private final WifiNative mWifiNative; + + private final WifiMetrics mWifiMetrics; + private final Listener mListener; + private final ScanRequestProxy mScanRequestProxy; - private static final String TAG = "ClientModeManager"; + private String mClientInterfaceName; - ClientModeManager() { + + ClientModeManager(Context context, @NonNull Looper looper, WifiNative wifiNative, + Listener listener, WifiMetrics wifiMetrics, ScanRequestProxy scanRequestProxy) { + mContext = context; + mWifiNative = wifiNative; + mListener = listener; + mWifiMetrics = wifiMetrics; + mScanRequestProxy = scanRequestProxy; + mStateMachine = new ClientModeStateMachine(looper); } /** * Start client mode. */ public void start() { - + mStateMachine.sendMessage(ClientModeStateMachine.CMD_START); } /** * Disconnect from any currently connected networks and stop client mode. */ public void stop() { + mStateMachine.sendMessage(ClientModeStateMachine.CMD_STOP); + } + + /** + * Listener for ClientMode state changes. + */ + public interface Listener { + /** + * Invoke when wifi state changes. + * @param state new wifi state + */ + void onStateChanged(int state); + } + + /** + * Update Wifi state and send the broadcast. + * @param newState new Wifi state + * @param currentState current wifi state + */ + private void updateWifiState(int newState, int currentState) { + mListener.onStateChanged(newState); + + if (newState == WifiManager.WIFI_STATE_UNKNOWN) { + // do not need to broadcast failure to system + return; + } + + final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private class ClientModeStateMachine 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 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; + + ClientModeStateMachine(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: + updateWifiState(WifiManager.WIFI_STATE_ENABLING, + WifiManager.WIFI_STATE_DISABLED); + + mClientInterfaceName = mWifiNative.setupInterfaceForClientMode( + false /* not low priority */, mWifiNativeInterfaceCallback); + if (TextUtils.isEmpty(mClientInterfaceName)) { + Log.e(TAG, "Failed to create ClientInterface. Sit in Idle"); + sendScanAvailableBroadcast(false); + updateWifiState(WifiManager.WIFI_STATE_UNKNOWN, + WifiManager.WIFI_STATE_ENABLING); + 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 client mode"); + sendScanAvailableBroadcast(true); + updateWifiState(WifiManager.WIFI_STATE_ENABLED, + WifiManager.WIFI_STATE_ENABLING); + } else { + // if the interface goes down we should exit and go back to idle state. + Log.d(TAG, "interface down! may need to restart ClientMode"); + updateWifiState(WifiManager.WIFI_STATE_UNKNOWN, + WifiManager.WIFI_STATE_UNKNOWN); + mStateMachine.sendMessage(CMD_STOP); + } + } + + @Override + public void enter() { + Log.d(TAG, "entering StartedState"); + mIfaceIsUp = false; + onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); + mScanRequestProxy.enableScanningForHiddenNetworks(true); + } + + @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 client mode."); + updateWifiState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + mWifiNative.teardownInterface(mClientInterfaceName); + transitionTo(mIdleState); + break; + case CMD_INTERFACE_STATUS_CHANGED: + boolean isUp = message.arg1 == 1; + onUpChanged(isUp); + break; + case CMD_WIFINATIVE_FAILURE: + Log.d(TAG, "WifiNative failure - may need to restart ClientMode!"); + updateWifiState(WifiManager.WIFI_STATE_UNKNOWN, + WifiManager.WIFI_STATE_UNKNOWN); + updateWifiState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + transitionTo(mIdleState); + break; + case CMD_INTERFACE_DESTROYED: + Log.d(TAG, "interface destroyed - client mode stopping"); + + updateWifiState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + 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() { + // let WifiScanner know that wifi is down. + sendScanAvailableBroadcast(false); + updateWifiState(WifiManager.WIFI_STATE_DISABLED, + WifiManager.WIFI_STATE_DISABLING); + mScanRequestProxy.enableScanningForHiddenNetworks(false); + 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); + } } } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 28e36169b..3b07e8250 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -450,6 +450,17 @@ public class WifiInjector { } /** + * Create a ClientModeManager + * + * @param listener listener for ClientModeManager state changes + * @return a new instance of ClientModeManager + */ + public ClientModeManager makeClientModeManager(ClientModeManager.Listener listener) { + return new ClientModeManager(mContext, mWifiStateMachineHandlerThread.getLooper(), + mWifiNative, listener, mWifiMetrics, mScanRequestProxy); + } + + /** * Create a WifiLog instance. * @param tag module name to include in all log messages */ diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java index 6baf5ad03..8eb521cbb 100644 --- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java +++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java @@ -80,6 +80,17 @@ public class WifiStateMachinePrime { // 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 WifiManager.SoftApCallback mSoftApCallback; /** @@ -259,10 +270,30 @@ public class WifiStateMachinePrime { } class ClientModeActiveState extends ModeActiveState { + private class ClientListener implements ClientModeManager.Listener { + @Override + public void onStateChanged(int state) { + Log.d(TAG, "State changed from client mode."); + if (state == WifiManager.WIFI_STATE_UNKNOWN) { + // error while setting up client mode or an unexpected failure. + mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED); + } else if (state == WifiManager.WIFI_STATE_DISABLED) { + // client mode stopped + mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED); + } else if (state == WifiManager.WIFI_STATE_ENABLED) { + // client mode is ready to go + Log.d(TAG, "client mode active"); + } else { + // only care if client mode stopped or started, dropping + } + } + } + @Override public void enter() { Log.d(TAG, "Entering ClientModeActiveState"); - mManager = new ClientModeManager(); + + mManager = mWifiInjector.makeClientModeManager(new ClientListener()); // DO NOT CALL START YET // mActiveModemanager.start(); mActiveModeManagers.add(mManager); |