summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorRebecca Silberstein <silberst@google.com>2018-01-24 02:27:42 -0800
committerRebecca Silberstein <silberst@google.com>2018-03-09 02:59:49 -0800
commite845cb0859529bf544208dc5d5711535231a04c0 (patch)
treeb04679105560d34f4d833b7ad97d0f0ec111004e /service
parent34226fdc35f33a5c3a95e0b86138c933c5682370 (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')
-rw-r--r--service/java/com/android/server/wifi/ClientModeManager.java247
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java11
-rw-r--r--service/java/com/android/server/wifi/WifiStateMachinePrime.java33
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);