Merge "[RTT2] Update RTT service name"
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index 2c16444..31124d5 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -79,6 +79,17 @@
contentObserver);
}
+ /**
+ * Helper method for classes to unregister a ContentObserver
+ * {@see ContentResolver#unregisterContentObserver(ContentObserver)}.
+ *
+ * @param context
+ * @param contentObserver
+ */
+ public void unregisterContentObserver(Context context, ContentObserver contentObserver) {
+ context.getContentResolver().unregisterContentObserver(contentObserver);
+ }
+
public IBinder getService(String serviceName) {
return ServiceManager.getService(serviceName);
}
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 429e3c3..2200618 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -23,21 +23,27 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
+import android.database.ContentObserver;
import android.net.InterfaceConfiguration;
import android.net.wifi.IApInterface;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.wifi.WifiNative.SoftApListener;
import com.android.server.wifi.util.ApConfigUtil;
@@ -51,8 +57,15 @@
public class SoftApManager implements ActiveModeManager {
private static final String TAG = "SoftApManager";
- private final Context mContext;
+ // Minimum limit to use for timeout delay if the value from overlay setting is too small.
+ private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000; // 10 minutes
+ @VisibleForTesting
+ public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
+ + " Soft AP Send Message Timeout";
+
+ private final Context mContext;
+ private final FrameworkFacade mFrameworkFacade;
private final WifiNative mWifiNative;
private final String mCountryCode;
@@ -72,8 +85,9 @@
private final int mMode;
private WifiConfiguration mApConfig;
- private int mNumAssociatedStations = 0;
-
+ /**
+ * Listener for soft AP events.
+ */
private final SoftApListener mSoftApListener = new SoftApListener() {
@Override
public void onNumAssociatedStationsChanged(int numStations) {
@@ -82,7 +96,6 @@
}
};
-
/**
* Listener for soft AP state changes.
*/
@@ -97,6 +110,7 @@
public SoftApManager(Context context,
Looper looper,
+ FrameworkFacade framework,
WifiNative wifiNative,
String countryCode,
Listener listener,
@@ -106,9 +120,8 @@
WifiApConfigStore wifiApConfigStore,
@NonNull SoftApModeConfiguration apConfig,
WifiMetrics wifiMetrics) {
- mStateMachine = new SoftApStateMachine(looper);
-
mContext = context;
+ mFrameworkFacade = framework;
mWifiNative = wifiNative;
mCountryCode = countryCode;
mListener = listener;
@@ -124,6 +137,7 @@
mApConfig = config;
}
mWifiMetrics = wifiMetrics;
+ mStateMachine = new SoftApStateMachine(looper);
}
/**
@@ -141,29 +155,6 @@
}
/**
- * Get number of stations associated with this soft AP
- */
- @VisibleForTesting
- public int getNumAssociatedStations() {
- return mNumAssociatedStations;
- }
-
- /**
- * Set number of stations associated with this soft AP
- * @param numStations Number of connected stations
- */
- private void setNumAssociatedStations(int numStations) {
- if (mNumAssociatedStations == numStations) {
- return;
- }
- mNumAssociatedStations = numStations;
- Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
-
- // TODO:(b/63906412) send it up to settings.
- mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations, mMode);
- }
-
- /**
* Update AP state.
* @param newState new AP state
* @param currentState current AP state
@@ -253,6 +244,8 @@
public static final int CMD_WIFICOND_BINDER_DEATH = 2;
public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4;
+ public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
+ public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
private final State mIdleState = new IdleState();
private final State mStartedState = new StartedState();
@@ -313,7 +306,6 @@
}
updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
WifiManager.WIFI_AP_STATE_DISABLED, 0);
- setNumAssociatedStations(0);
if (!mWifiNative.registerWificondDeathHandler(mWificondDeathRecipient)) {
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
WifiManager.WIFI_AP_STATE_ENABLING,
@@ -375,6 +367,92 @@
private class StartedState extends State {
private boolean mIfaceIsUp;
+ private int mNumAssociatedStations;
+
+ private boolean mTimeoutEnabled;
+ private int mTimeoutDelay;
+ private WakeupMessage mSoftApTimeoutMessage;
+ private SoftApTimeoutEnabledSettingObserver mSettingObserver;
+
+ /**
+ * Observer for timeout settings changes.
+ */
+ private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
+ SoftApTimeoutEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ mFrameworkFacade.registerContentObserver(mContext,
+ Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
+ true, this);
+ mTimeoutEnabled = getValue();
+ }
+
+ public void unregister() {
+ mFrameworkFacade.unregisterContentObserver(mContext, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
+ getValue() ? 1 : 0);
+ }
+
+ private boolean getValue() {
+ boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
+ Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
+ return enabled;
+ }
+ }
+
+ private int getConfigSoftApTimeoutDelay() {
+ int delay = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_soft_ap_timeout_delay);
+ if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
+ delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
+ Log.w(TAG, "Overriding timeout delay with minimum limit value");
+ }
+ Log.d(TAG, "Timeout delay: " + delay);
+ return delay;
+ }
+
+ private void scheduleTimeoutMessage() {
+ if (!mTimeoutEnabled) {
+ return;
+ }
+ mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
+ Log.d(TAG, "Timeout message scheduled");
+ }
+
+ private void cancelTimeoutMessage() {
+ mSoftApTimeoutMessage.cancel();
+ Log.d(TAG, "Timeout message canceled");
+ }
+
+ /**
+ * Set number of stations associated with this soft AP
+ * @param numStations Number of connected stations
+ */
+ private void setNumAssociatedStations(int numStations) {
+ if (mNumAssociatedStations == numStations) {
+ return;
+ }
+ mNumAssociatedStations = numStations;
+ Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
+
+ // TODO:(b/63906412) send it up to settings.
+ mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations,
+ mMode);
+
+ if (mNumAssociatedStations == 0) {
+ scheduleTimeoutMessage();
+ } else {
+ cancelTimeoutMessage();
+ }
+ }
+
private void onUpChanged(boolean isUp) {
if (isUp == mIfaceIsUp) {
return; // no change
@@ -388,9 +466,7 @@
} else {
// TODO: handle the case where the interface was up, but goes down
}
-
mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
- setNumAssociatedStations(0);
}
@Override
@@ -404,6 +480,30 @@
if (config != null) {
onUpChanged(config.isUp());
}
+
+ mTimeoutDelay = getConfigSoftApTimeoutDelay();
+ Handler handler = mStateMachine.getHandler();
+ mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
+ SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
+ SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
+ mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
+
+ if (mSettingObserver != null) {
+ mSettingObserver.register();
+ }
+ Log.d(TAG, "Resetting num stations on start");
+ mNumAssociatedStations = 0;
+ scheduleTimeoutMessage();
+ }
+
+ @Override
+ public void exit() {
+ if (mSettingObserver != null) {
+ mSettingObserver.unregister();
+ }
+ Log.d(TAG, "Resetting num stations on stop");
+ mNumAssociatedStations = 0;
+ cancelTimeoutMessage();
}
@Override
@@ -414,8 +514,22 @@
Log.e(TAG, "Invalid number of associated stations: " + message.arg1);
break;
}
+ Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED");
setNumAssociatedStations(message.arg1);
break;
+ case CMD_TIMEOUT_TOGGLE_CHANGED:
+ boolean isEnabled = (message.arg1 == 1);
+ if (mTimeoutEnabled == isEnabled) {
+ break;
+ }
+ mTimeoutEnabled = isEnabled;
+ if (!mTimeoutEnabled) {
+ cancelTimeoutMessage();
+ }
+ if (mTimeoutEnabled && mNumAssociatedStations == 0) {
+ scheduleTimeoutMessage();
+ }
+ break;
case CMD_INTERFACE_STATUS_CHANGED:
if (message.obj != mNetworkObserver) {
// This is from some time before the most recent configuration.
@@ -427,11 +541,21 @@
case CMD_START:
// Already started, ignore this command.
break;
+ case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
+ if (!mTimeoutEnabled) {
+ Log.wtf(TAG, "Timeout message received while timeout is disabled."
+ + " Dropping.");
+ break;
+ }
+ if (mNumAssociatedStations != 0) {
+ Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
+ break;
+ }
+ Log.i(TAG, "Timeout message received. Stopping soft AP.");
case CMD_WIFICOND_BINDER_DEATH:
case CMD_STOP:
updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
WifiManager.WIFI_AP_STATE_ENABLED, 0);
- setNumAssociatedStations(0);
stopSoftAp();
if (message.what == CMD_WIFICOND_BINDER_DEATH) {
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
index f839ac8..5775117 100644
--- a/service/java/com/android/server/wifi/WakeupConfigStoreData.java
+++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
@@ -45,8 +45,10 @@
/**
* Interface defining a data source for the store data.
+ *
+ * @param <T> Type of data source
*/
- interface DataSource<T> {
+ public interface DataSource<T> {
/**
* Returns the data from the data source.
*/
diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java
index a3c095a..8787bfd 100644
--- a/service/java/com/android/server/wifi/WakeupController.java
+++ b/service/java/com/android/server/wifi/WakeupController.java
@@ -24,6 +24,9 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* WakeupController is responsible managing Auto Wifi.
*
@@ -38,16 +41,26 @@
private final Handler mHandler;
private final FrameworkFacade mFrameworkFacade;
private final ContentObserver mContentObserver;
+ private final WakeupLock mWakeupLock;
+ private final WifiConfigManager mWifiConfigManager;
/** Whether this feature is enabled in Settings. */
private boolean mWifiWakeupEnabled;
+ /** Whether the WakeupController is currently active. */
+ private boolean mIsActive = false;
+
public WakeupController(
Context context,
Looper looper,
+ WakeupLock wakeupLock,
+ WifiConfigManager wifiConfigManager,
+ WifiConfigStore wifiConfigStore,
FrameworkFacade frameworkFacade) {
mContext = context;
mHandler = new Handler(looper);
+ mWakeupLock = wakeupLock;
+ mWifiConfigManager = wifiConfigManager;
mFrameworkFacade = frameworkFacade;
mContentObserver = new ContentObserver(mHandler) {
@Override
@@ -59,6 +72,19 @@
mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
Settings.Global.WIFI_WAKEUP_ENABLED), true, mContentObserver);
mContentObserver.onChange(false /* selfChange */);
+
+ // registering the store data here has the effect of reading the persisted value of the
+ // data sources after system boot finishes
+ WakeupConfigStoreData wakeupConfigStoreData =
+ new WakeupConfigStoreData(new IsActiveDataSource(), mWakeupLock.getDataSource());
+ wifiConfigStore.registerStoreData(wakeupConfigStoreData);
+ }
+
+ private void setActive(boolean isActive) {
+ if (mIsActive != isActive) {
+ mIsActive = isActive;
+ mWifiConfigManager.saveToStore(false /* forceWrite */);
+ }
}
/**
@@ -71,4 +97,26 @@
boolean isEnabled() {
return mWifiWakeupEnabled;
}
+
+ /** Dumps wakeup controller state. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Dump of WakeupController");
+ pw.println("mWifiWakeupEnabled: " + mWifiWakeupEnabled);
+ pw.println("USE_PLATFORM_WIFI_WAKE: " + USE_PLATFORM_WIFI_WAKE);
+ pw.println("mIsActive: " + mIsActive);
+ mWakeupLock.dump(fd, pw, args);
+ }
+
+ private class IsActiveDataSource implements WakeupConfigStoreData.DataSource<Boolean> {
+
+ @Override
+ public Boolean getData() {
+ return mIsActive;
+ }
+
+ @Override
+ public void setData(Boolean data) {
+ mIsActive = data;
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WakeupLock.java b/service/java/com/android/server/wifi/WakeupLock.java
index 73cda91..1fcd9f8 100644
--- a/service/java/com/android/server/wifi/WakeupLock.java
+++ b/service/java/com/android/server/wifi/WakeupLock.java
@@ -17,12 +17,16 @@
package com.android.server.wifi;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
/**
* A lock to determine whether Auto Wifi can re-enable Wifi.
@@ -31,18 +35,24 @@
*/
public class WakeupLock {
+ private static final String TAG = WakeupLock.class.getSimpleName();
+
@VisibleForTesting
static final int CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 3;
- private Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>();
- // TODO(easchwar) read initial value of mLockedNetworks from file
- public WakeupLock() {
+ private final WifiConfigManager mWifiConfigManager;
+ private final Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>();
+
+ public WakeupLock(WifiConfigManager wifiConfigManager) {
+ mWifiConfigManager = wifiConfigManager;
}
/**
* Initializes the WakeupLock with the given {@link ScanResultMatchInfo} list.
*
+ * <p>This saves the wakeup lock to the store.
+ *
* @param scanResultList list of ScanResultMatchInfos to start the lock with
*/
public void initialize(Collection<ScanResultMatchInfo> scanResultList) {
@@ -50,17 +60,23 @@
for (ScanResultMatchInfo scanResultMatchInfo : scanResultList) {
mLockedNetworks.put(scanResultMatchInfo, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
}
+
+ Log.d(TAG, "Lock initialized. Number of networks: " + mLockedNetworks.size());
+
+ mWifiConfigManager.saveToStore(false /* forceWrite */);
}
/**
* Updates the lock with the given {@link ScanResultMatchInfo} list.
*
* <p>If a network in the lock is not present in the list, reduce the number of scans
- * required to evict by one. Remove any entries in the list with 0 scans required to evict.
+ * required to evict by one. Remove any entries in the list with 0 scans required to evict. If
+ * any entries in the lock are removed, the store is updated.
*
* @param scanResultList list of present ScanResultMatchInfos to update the lock with
*/
public void update(Collection<ScanResultMatchInfo> scanResultList) {
+ boolean hasChanged = false;
Iterator<Map.Entry<ScanResultMatchInfo, Integer>> it =
mLockedNetworks.entrySet().iterator();
while (it.hasNext()) {
@@ -76,9 +92,13 @@
entry.setValue(entry.getValue() - 1);
if (entry.getValue() <= 0) {
it.remove();
+ hasChanged = true;
}
}
- // TODO(easchwar) write the updated list to file
+
+ if (hasChanged) {
+ mWifiConfigManager.saveToStore(false /* forceWrite */);
+ }
}
/**
@@ -87,4 +107,36 @@
public boolean isEmpty() {
return mLockedNetworks.isEmpty();
}
+
+ /** Returns the data source for the WakeupLock config store data. */
+ public WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> getDataSource() {
+ return new WakeupLockDataSource();
+ }
+
+ /** Dumps wakeup lock contents. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WakeupLock: ");
+ pw.println("Locked networks: " + mLockedNetworks.size());
+ for (Map.Entry<ScanResultMatchInfo, Integer> entry : mLockedNetworks.entrySet()) {
+ pw.println(entry.getKey() + ", scans to evict: " + entry.getValue());
+ }
+ }
+
+ private class WakeupLockDataSource
+ implements WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> {
+
+ @Override
+ public Set<ScanResultMatchInfo> getData() {
+ return mLockedNetworks.keySet();
+ }
+
+ @Override
+ public void setData(Set<ScanResultMatchInfo> data) {
+ mLockedNetworks.clear();
+ for (ScanResultMatchInfo network : data) {
+ mLockedNetworks.put(network, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
+ }
+
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index f6770a5..dc48163 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -230,7 +230,8 @@
new OpenNetworkRecommender(),
new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
mWakeupController = new WakeupController(mContext,
- mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade);
+ mWifiStateMachineHandlerThread.getLooper(), new WakeupLock(mWifiConfigManager),
+ mWifiConfigManager, mWifiConfigStore, mFrameworkFacade);
mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
@@ -380,10 +381,9 @@
@NonNull IApInterface apInterface,
@NonNull String ifaceName,
@NonNull SoftApModeConfiguration config) {
- return new SoftApManager(mContext, mWifiServiceHandlerThread.getLooper(),
- mWifiNative, mCountryCode.getCountryCode(),
- listener, apInterface, ifaceName, nmService,
- mWifiApConfigStore, config, mWifiMetrics);
+ return new SoftApManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
+ mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), listener, apInterface,
+ ifaceName, nmService, mWifiApConfigStore, config, mWifiMetrics);
}
/**
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 2f1b550..3118761 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -152,6 +152,105 @@
}
}
+ /**
+ * TODO(b/69426063): NEW API Surface for interface management. This will eventually
+ * deprecate the other interface management API's above. But, for now there will be
+ * some duplication to ease transition.
+ */
+ /**
+ * Initialize the native modules.
+ *
+ * @return true on success, false otherwise.
+ */
+ public boolean initialize() {
+ return false;
+ }
+
+ /**
+ * Callback to notify when the status of one of the native daemons
+ * (wificond, wpa_supplicant & vendor HAL) changes.
+ */
+ public interface StatusListener {
+ /**
+ * @param allReady Indicates if all the native daemons are ready for operation or not.
+ */
+ void onStatusChanged(boolean allReady);
+ }
+
+ /**
+ * Register a StatusListener to get notified about any status changes from the native daemons.
+ *
+ * It is safe to re-register the same callback object - duplicates are detected and only a
+ * single copy kept.
+ *
+ * @param listener StatusListener listener object.
+ */
+ public void registerStatusListener(@NonNull StatusListener listener) {
+ }
+
+ /**
+ * Callback to notify when the associated interface is destroyed, up or down.
+ */
+ public interface InterfaceCallback {
+ /**
+ * Interface destroyed by HalDeviceManager.
+ *
+ * @param ifaceName Name of the iface.
+ */
+ void onDestroyed(String ifaceName);
+
+ /**
+ * Interface is up.
+ *
+ * @param ifaceName Name of the iface.
+ */
+ void onUp(String ifaceName);
+
+ /**
+ * Interface is down.
+ *
+ * @param ifaceName Name of the iface.
+ */
+ void onDown(String ifaceName);
+ }
+
+ /**
+ * Setup an interface for Client mode operations.
+ *
+ * This method configures an interface in STA mode in all the native daemons
+ * (wificond, wpa_supplicant & vendor HAL).
+ *
+ * @param interfaceCallback Associated callback for notifying status changes for the iface.
+ * @return Returns the name of the allocated interface, will be null on failure.
+ */
+ public String setupInterfaceForClientMode(@NonNull InterfaceCallback interfaceCallback) {
+ return null;
+ }
+
+ /**
+ * Setup an interface for Soft AP mode operations.
+ *
+ * This method configures an interface in AP mode in all the native daemons
+ * (wificond, wpa_supplicant & vendor HAL).
+ *
+ * @param interfaceCallback Associated callback for notifying status changes for the iface.
+ * @return Returns the name of the allocated interface, will be null on failure.
+ */
+ public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
+ return null;
+ }
+
+ /**
+ * Teardown an interface in Client/AP mode.
+ *
+ * This method tears down the associated interface from all the native daemons
+ * (wificond, wpa_supplicant & vendor HAL).
+ *
+ * @param ifaceName Name of the interface.
+ */
+ public void teardownInterface(@NonNull String ifaceName) {
+ }
+
/********************************************************
* Wificond operations
********************************************************/
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 0adef36..1521bb9 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -2281,6 +2281,7 @@
} else {
pw.println("mWifiConnectivityManager is not initialized");
}
+ mWifiInjector.getWakeupController().dump(fd, pw, args);
}
public void handleUserSwitch(int userId) {
@@ -3249,16 +3250,18 @@
|| stateChangeResult.wifiSsid.toString().isEmpty()) && isLinkDebouncing()) {
return state;
}
- // Network id is only valid when we start connecting
+ // Network id and SSID are only valid when we start connecting
if (SupplicantState.isConnecting(state)) {
mWifiInfo.setNetworkId(lookupFrameworkNetworkId(stateChangeResult.networkId));
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ mWifiInfo.setSSID(stateChangeResult.wifiSsid);
} else {
+ // Reset parameters according to WifiInfo.reset()
mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
}
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- mWifiInfo.setSSID(stateChangeResult.wifiSsid);
-
final WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
mWifiInfo.setEphemeral(config.ephemeral);
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
index c49b645..de970ee 100644
--- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.INetworkManagementService;
@@ -26,6 +25,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
@@ -47,12 +47,13 @@
private final WifiInjector mWifiInjector;
private final Looper mLooper;
+ private final WifiNative mWifiNative;
private final INetworkManagementService mNMService;
- private IWificond mWificond;
-
private Queue<SoftApModeConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>();
+ private String mInterfaceName;
+
/* The base for wifi message types */
static final int BASE = Protocol.BASE_WIFI;
@@ -67,22 +68,17 @@
WifiStateMachinePrime(WifiInjector wifiInjector,
Looper looper,
+ WifiNative wifiNative,
INetworkManagementService nmService) {
mWifiInjector = wifiInjector;
mLooper = looper;
+ mWifiNative = wifiNative;
mNMService = nmService;
- // Clean up existing interfaces in wificond.
- // This ensures that the framework and wificond are in a consistent state after a framework
- // restart.
- try {
- mWificond = mWifiInjector.makeWificond();
- if (mWificond != null) {
- mWificond.tearDownInterfaces();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "wificond died during framework startup");
- }
+ mInterfaceName = mWifiNative.getInterfaceName();
+
+ // make sure we do not have leftover state in the event of a restart
+ mWifiNative.tearDown();
}
/**
@@ -211,23 +207,13 @@
return HANDLED;
}
- private void tearDownInterfaces() {
- if (mWificond != null) {
- try {
- mWificond.tearDownInterfaces();
- } catch (RemoteException e) {
- // There is very little we can do here
- Log.e(TAG, "Failed to tear down interfaces via wificond");
- }
- mWificond = null;
- }
- return;
+ private void cleanup() {
+ mWifiNative.tearDown();
}
class ClientModeState extends State {
@Override
public void enter() {
- mWificond = mWifiInjector.makeWificond();
}
@Override
@@ -240,7 +226,7 @@
@Override
public void exit() {
- tearDownInterfaces();
+ cleanup();
}
}
@@ -280,17 +266,27 @@
// Continue with setup since we are changing modes
mApInterface = null;
+ Pair<Integer, IApInterface> statusAndInterface =
+ mWifiNative.setupForSoftApMode(mInterfaceName);
+ if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
+ mApInterface = statusAndInterface.second;
+ } else {
+ // TODO: incorporate metrics - or this particular one will be done in WifiNative
+ //incrementMetricsForSetupFailure(statusAndInterface.first);
+ }
+ if (mApInterface == null) {
+ // TODO: update battery stats that a failure occured - best to do in
+ // initialization Failed. Keeping line copied from WSM for a reference
+ //setWifiApState(WIFI_AP_STATE_FAILED,
+ // WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
+ initializationFailed("Could not get IApInterface instance");
+ return;
+ }
+
try {
- mWificond = mWifiInjector.makeWificond();
- mApInterface = mWificond.createApInterface(
- mWifiInjector.getWifiNative().getInterfaceName());
mIfaceName = mApInterface.getInterfaceName();
} catch (RemoteException e) {
- initializationFailed(
- "Could not get IApInterface instance or its name from wificond");
- return;
- } catch (NullPointerException e) {
- initializationFailed("wificond failure when initializing softap mode");
+ initializationFailed("Could not get IApInterface name");
return;
}
mModeStateMachine.transitionTo(mSoftAPModeActiveState);
@@ -325,7 +321,7 @@
@Override
public void exit() {
- tearDownInterfaces();
+ cleanup();
}
protected IApInterface getInterface() {
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index 421d9ac..62cc3ec 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -329,8 +329,8 @@
enforceNetworkStackPermission();
}
- if (message != null
- && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
+ if (message != null && message.length
+ > mStateManager.getCharacteristics().getMaxServiceSpecificInfoLength()) {
throw new IllegalArgumentException(
"Message length longer than supported by device characteristics");
}
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index 11e508d..b5c5844 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -68,7 +68,9 @@
android.test.runner \
wifi-service \
services \
- android.hidl.manager-V1.0-java
+ android.hidl.manager-V1.0-java \
+ android.test.base \
+ android.test.mock
# These must be explicitly included because they are not normally accessible
# from apps.
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index cda9cf5..0c35904 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -33,17 +33,24 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
+import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
+import android.database.ContentObserver;
import android.net.InterfaceConfiguration;
+import android.net.Uri;
import android.net.wifi.IApInterface;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.INetworkManagementService;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.R;
+import com.android.internal.util.WakeupMessage;
import com.android.server.net.BaseNetworkObserver;
import org.junit.Before;
@@ -77,11 +84,16 @@
private final WifiConfiguration mDefaultApConfig = createDefaultApConfig();
+ private ContentObserver mContentObserver;
+ private TestLooper mLooper;
+ private TestAlarmManager mAlarmManager;
+
@Mock Context mContext;
- TestLooper mLooper;
+ @Mock Resources mResources;
@Mock WifiNative mWifiNative;
@Mock SoftApManager.Listener mListener;
@Mock InterfaceConfiguration mInterfaceConfiguration;
+ @Mock FrameworkFacade mFrameworkFacade;
@Mock IApInterface mApInterface;
@Mock INetworkManagementService mNmService;
@Mock WifiApConfigStore mWifiApConfigStore;
@@ -104,6 +116,15 @@
when(mWifiNative.startSoftAp(any(), any())).thenReturn(true);
when(mWifiNative.stopSoftAp()).thenReturn(true);
when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(true);
+
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+ mAlarmManager = new TestAlarmManager();
+ when(mContext.getSystemService(Context.ALARM_SERVICE))
+ .thenReturn(mAlarmManager.getAlarmManager());
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getInteger(R.integer.config_wifi_framework_soft_ap_timeout_delay))
+ .thenReturn(600000);
}
private WifiConfiguration createDefaultApConfig() {
@@ -118,6 +139,7 @@
}
SoftApManager newSoftApManager = new SoftApManager(mContext,
mLooper.getLooper(),
+ mFrameworkFacade,
mWifiNative,
TEST_COUNTRY_CODE,
mListener,
@@ -189,6 +211,7 @@
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
SoftApManager newSoftApManager = new SoftApManager(mContext,
mLooper.getLooper(),
+ mFrameworkFacade,
mWifiNative,
TEST_COUNTRY_CODE,
mListener,
@@ -232,6 +255,7 @@
SoftApManager newSoftApManager = new SoftApManager(mContext,
mLooper.getLooper(),
+ mFrameworkFacade,
mWifiNative,
null,
mListener,
@@ -275,6 +299,7 @@
SoftApManager newSoftApManager = new SoftApManager(mContext,
mLooper.getLooper(),
+ mFrameworkFacade,
mWifiNative,
TEST_COUNTRY_CODE,
mListener,
@@ -309,8 +334,10 @@
when(mWifiNative.startSoftAp(any(), any())).thenReturn(false);
SoftApModeConfiguration softApModeConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mDefaultApConfig);
+
SoftApManager newSoftApManager = new SoftApManager(mContext,
mLooper.getLooper(),
+ mFrameworkFacade,
mWifiNative,
TEST_COUNTRY_CODE,
mListener,
@@ -415,30 +442,11 @@
mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
TEST_NUM_CONNECTED_CLIENTS);
mLooper.dispatchAll();
- assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(TEST_NUM_CONNECTED_CLIENTS,
apConfig.getTargetMode());
}
@Test
- public void handlesNumAssociatedStationsWhenNotStarted() throws Exception {
- SoftApModeConfiguration apConfig =
- new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
- startSoftApAndVerifyEnabled(apConfig);
- mSoftApManager.stop();
- mLooper.dispatchAll();
-
- mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
- TEST_NUM_CONNECTED_CLIENTS);
- mLooper.dispatchAll();
- /* Verify numAssociatedStations is not updated when soft AP is not started */
- assertEquals(0, mSoftApManager.getNumAssociatedStations());
- verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
- verify(mWifiMetrics, never()).addSoftApNumAssociatedStationsChangedEvent(
- TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
- }
-
- @Test
public void handlesInvalidNumAssociatedStations() throws Exception {
SoftApModeConfiguration apConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
@@ -449,41 +457,155 @@
/* Invalid values should be ignored */
mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(-1);
mLooper.dispatchAll();
- assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
verify(mWifiMetrics, times(1)).addSoftApNumAssociatedStationsChangedEvent(
TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
}
@Test
- public void resetsNumAssociatedStationsWhenStopped() throws Exception {
+ public void schedulesTimeoutTimerOnStart() throws Exception {
SoftApModeConfiguration apConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
startSoftApAndVerifyEnabled(apConfig);
- mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
- TEST_NUM_CONNECTED_CLIENTS);
- mSoftApManager.stop();
- mLooper.dispatchAll();
- assertEquals(0, mSoftApManager.getNumAssociatedStations());
- verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+ // Verify timer is scheduled
+ verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+ eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
}
@Test
- public void resetsNumAssociatedStationsOnFailure() throws Exception {
+ public void cancelsTimeoutTimerOnStop() throws Exception {
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+ mSoftApManager.stop();
+ mLooper.dispatchAll();
+
+ // Verify timer is canceled
+ verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+ }
+
+ @Test
+ public void cancelsTimeoutTimerOnNewClientsConnect() throws Exception {
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+ mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+ TEST_NUM_CONNECTED_CLIENTS);
+ mLooper.dispatchAll();
+
+ // Verify timer is canceled
+ verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+ }
+
+ @Test
+ public void schedulesTimeoutTimerWhenAllClientsDisconnect() throws Exception {
SoftApModeConfiguration apConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
startSoftApAndVerifyEnabled(apConfig);
mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
TEST_NUM_CONNECTED_CLIENTS);
- /* Force soft AP to fail */
- mDeathListenerCaptor.getValue().onDeath();
mLooper.dispatchAll();
- verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
- WifiManager.SAP_START_FAILURE_GENERAL);
+ // Verify timer is canceled at this point
+ verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
- assertEquals(0, mSoftApManager.getNumAssociatedStations());
- verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+ mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+ mLooper.dispatchAll();
+ // Verify timer is scheduled again
+ verify(mAlarmManager.getAlarmManager(), times(2)).setExact(anyInt(), anyLong(),
+ eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+ }
+
+ @Test
+ public void stopsSoftApOnTimeoutMessage() throws Exception {
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+
+ mAlarmManager.dispatch(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).stopSoftAp();
+ }
+
+ @Test
+ public void cancelsTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+ mContentObserver.onChange(false);
+ mLooper.dispatchAll();
+
+ // Verify timer is canceled
+ verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+ }
+
+ @Test
+ public void schedulesTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+ // start with timeout toggle disabled
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+ mContentObserver.onChange(false);
+ mLooper.dispatchAll();
+
+ // Verify timer is scheduled
+ verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+ eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+ }
+
+ @Test
+ public void doesNotScheduleTimeoutTimerOnStartWhenTimeoutIsDisabled() throws Exception {
+ // start with timeout toggle disabled
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+
+ // Verify timer is not scheduled
+ verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+ eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+ }
+
+ @Test
+ public void doesNotScheduleTimeoutTimerWhenAllClientsDisconnectButTimeoutIsDisabled()
+ throws Exception {
+ // start with timeout toggle disabled
+ when(mFrameworkFacade.getIntegerSetting(
+ mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+ // add some clients
+ mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+ TEST_NUM_CONNECTED_CLIENTS);
+ mLooper.dispatchAll();
+ // remove all clients
+ mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+ mLooper.dispatchAll();
+ // Verify timer is not scheduled
+ verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+ eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+ }
+
+ @Test
+ public void unregistersSettingsObserverOnStop() throws Exception {
+ SoftApModeConfiguration apConfig =
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ startSoftApAndVerifyEnabled(apConfig);
+ mSoftApManager.stop();
+ mLooper.dispatchAll();
+
+ verify(mFrameworkFacade).unregisterContentObserver(eq(mContext), eq(mContentObserver));
}
/** Starts soft AP and verifies that it is enabled successfully. */
@@ -506,6 +628,8 @@
expectedConfig = new WifiConfiguration(config);
}
+ ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(
+ ContentObserver.class);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mSoftApManager.start();
@@ -530,8 +654,10 @@
checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED,
WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
softApConfig.getTargetMode());
- assertEquals(0, mSoftApManager.getNumAssociatedStations());
verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.mTargetMode);
+ verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true),
+ observerCaptor.capture());
+ mContentObserver = observerCaptor.getValue();
}
/** Verifies that soft AP was not disabled. */
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
index 742c520..5e45570 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -29,12 +31,18 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
/**
* Unit tests for {@link WakeupController}.
*/
public class WakeupControllerTest {
@Mock private Context mContext;
+ @Mock private WakeupLock mWakeupLock;
+ @Mock private WifiConfigManager mWifiConfigManager;
+ @Mock private WifiConfigStore mWifiConfigStore;
@Mock private FrameworkFacade mFrameworkFacade;
private TestLooper mLooper;
@@ -47,6 +55,11 @@
mLooper = new TestLooper();
}
+ private WakeupController newWakeupController() {
+ return new WakeupController(mContext, mLooper.getLooper(), mWakeupLock, mWifiConfigManager,
+ mWifiConfigStore, mFrameworkFacade);
+ }
+
/**
* Verify WakeupController is enabled when the settings toggle is true.
*/
@@ -54,8 +67,7 @@
public void verifyEnabledWhenToggledOn() {
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(1);
- mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
- mFrameworkFacade);
+ mWakeupController = newWakeupController();
assertTrue(mWakeupController.isEnabled());
}
@@ -67,9 +79,30 @@
public void verifyDisabledWhenToggledOff() {
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(0);
- mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
- mFrameworkFacade);
+ mWakeupController = newWakeupController();
assertFalse(mWakeupController.isEnabled());
}
+
+ /**
+ * Verify WakeupController registers its store data with the WifiConfigStore on construction.
+ */
+ @Test
+ public void registersWakeupConfigStoreData() {
+ mWakeupController = newWakeupController();
+ verify(mWifiConfigStore).registerStoreData(any(WakeupConfigStoreData.class));
+ }
+
+ /**
+ * Verify that dump calls also dump the state of the WakeupLock.
+ */
+ @Test
+ public void dumpIncludesWakeupLock() {
+ mWakeupController = newWakeupController();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ PrintWriter writer = new PrintWriter(stream);
+ mWakeupController.dump(null, writer, null);
+
+ verify(mWakeupLock).dump(null, writer, null);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
index bad9e3a..7144ecf 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
@@ -18,9 +18,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collection;
@@ -35,6 +39,8 @@
private static final String SSID_1 = "ssid1";
private static final String SSID_2 = "ssid2";
+ @Mock private WifiConfigManager mWifiConfigManager;
+
private ScanResultMatchInfo mNetwork1;
private ScanResultMatchInfo mNetwork2;
private WakeupLock mWakeupLock;
@@ -44,6 +50,8 @@
*/
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
mNetwork1 = new ScanResultMatchInfo();
mNetwork1.networkSsid = SSID_1;
mNetwork1.networkType = ScanResultMatchInfo.NETWORK_TYPE_OPEN;
@@ -52,7 +60,7 @@
mNetwork2.networkSsid = SSID_2;
mNetwork2.networkType = ScanResultMatchInfo.NETWORK_TYPE_EAP;
- mWakeupLock = new WakeupLock();
+ mWakeupLock = new WakeupLock(mWifiConfigManager);
}
/**
@@ -69,6 +77,18 @@
}
/**
+ * Updates the lock enough times to evict any networks not passed in.
+ *
+ * <p>It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with
+ * the given network list. It does not make any assertions about the state of the lock.
+ */
+ private void updateEnoughTimesToEvictWithoutAsserts(Collection<ScanResultMatchInfo> networks) {
+ for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) {
+ mWakeupLock.update(networks);
+ }
+ }
+
+ /**
* Verify that the WakeupLock is not empty immediately after being initialized with networks.
*/
@Test
@@ -161,4 +181,34 @@
updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2));
assertTrue(mWakeupLock.isEmpty());
}
+
+ /**
+ * Verify that initializing the lock persists the SSID list to the config store.
+ */
+ @Test
+ public void initializeShouldSaveSsidsToStore() {
+ mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+ verify(mWifiConfigManager).saveToStore(eq(false));
+ }
+
+ /**
+ * Verify that update saves to store if the lock changes.
+ */
+ @Test
+ public void updateShouldOnlySaveIfLockChanges() {
+ mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+ updateEnoughTimesToEvictWithoutAsserts(Collections.emptyList());
+
+ // need exactly 2 invocations: 1 for initialize, 1 for successful update
+ verify(mWifiConfigManager, times(2)).saveToStore(eq(false));
+ }
+
+ /**
+ * Verify that update does not save to store if the lock does not change.
+ */
+ @Test
+ public void updateShouldNotSaveIfLockDoesNotChange() {
+ mWakeupLock.update(Collections.singletonList(mNetwork1));
+ verify(mWifiConfigManager, never()).saveToStore(anyBoolean());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
index 2e26769..f2d6cc6 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
@@ -20,13 +20,13 @@
import static org.mockito.Mockito.*;
import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.INetworkManagementService;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import android.util.Pair;
import org.junit.After;
import org.junit.Before;
@@ -56,7 +56,6 @@
@Mock WifiNative mWifiNative;
@Mock WifiApConfigStore mWifiApConfigStore;
TestLooper mLooper;
- @Mock IWificond mWificond;
@Mock IApInterface mApInterface;
@Mock INetworkManagementService mNMService;
@Mock SoftApManager mSoftApManager;
@@ -76,11 +75,15 @@
when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
mWifiStateMachinePrime = createWifiStateMachinePrime();
+
+ // creating a new WSMP cleans up any existing interfaces, check and reset expectations
+ verifyCleanupCalled();
+ reset(mWifiNative);
}
private WifiStateMachinePrime createWifiStateMachinePrime() {
- when(mWifiInjector.makeWificond()).thenReturn(null);
- return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+ return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+ mWifiNative, mNMService);
}
/**
@@ -103,8 +106,8 @@
*/
private void enterSoftApActiveMode(SoftApModeConfiguration softApConfig) throws Exception {
String fromState = mWifiStateMachinePrime.getCurrentMode();
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
- when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, mApInterface));
when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
doAnswer(
new Answer<Object>() {
@@ -126,24 +129,30 @@
mLooper.dispatchAll();
Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState);
if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) {
- verify(mWificond).tearDownInterfaces();
+ verifyCleanupCalled();
}
assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
verify(mSoftApManager).start();
}
+ private void verifyCleanupCalled() {
+ // for now, this is a single call, but make a helper to avoid adding any additional cleanup
+ // checks
+ verify(mWifiNative).tearDown();
+ }
+
/**
- * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in
- * the retrieved Wificond instance are cleaned up.
+ * Test that when a new instance of WifiStateMachinePrime is created, any existing
+ * resources in WifiNative are cleaned up.
* Expectations: When the new WifiStateMachinePrime instance is created a call to
- * Wificond.tearDownInterfaces() is made.
+ * WifiNative.tearDown() is made.
*/
@Test
- public void testWificondExistsOnStartup() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ public void testCleanupOnStart() throws Exception {
WifiStateMachinePrime testWifiStateMachinePrime =
- new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
- verify(mWificond).tearDownInterfaces();
+ new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+ mWifiNative, mNMService);
+ verifyCleanupCalled();
}
/**
@@ -157,12 +166,10 @@
/**
* Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state.
- * Expectations: When going from one state to another, any interfaces that are still up are torn
- * down.
+ * Expectations: When going from one state to another, cleanup will be called
*/
@Test
public void testEnterSoftApModeFromDifferentState() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
mWifiStateMachinePrime.enterClientMode();
mLooper.dispatchAll();
assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
@@ -179,7 +186,7 @@
mWifiStateMachinePrime.disableWifi();
mLooper.dispatchAll();
verify(mSoftApManager).stop();
- verify(mWificond).tearDownInterfaces();
+ verifyCleanupCalled();
assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
}
@@ -188,8 +195,10 @@
*/
@Test
public void testDisableWifiFromSoftApModeState() throws Exception {
- // Use a failure getting wificond to stay in the SoftAPModeState
- when(mWifiInjector.makeWificond()).thenReturn(null);
+ // Use a failure getting the interface to stay in SoftApMode
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_FAILURE_HAL, null));
+
mWifiStateMachinePrime.enterSoftAPMode(
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
mLooper.dispatchAll();
@@ -197,7 +206,7 @@
mWifiStateMachinePrime.disableWifi();
mLooper.dispatchAll();
- // mWificond will be null due to this test, no call to tearDownInterfaces here.
+ verifyCleanupCalled();
assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
}
@@ -213,26 +222,11 @@
mWifiStateMachinePrime.enterClientMode();
mLooper.dispatchAll();
verify(mSoftApManager).stop();
- verify(mWificond).tearDownInterfaces();
+ verifyCleanupCalled();
assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
}
/**
- * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to
- * wificond.
- * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in
- * the SoftApModeState.
- */
- @Test
- public void testWificondNullWhenSwitchingToApMode() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(null);
- mWifiStateMachinePrime.enterSoftAPMode(
- new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
- mLooper.dispatchAll();
- assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
- }
-
- /**
* Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface
* from wificond.
* Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should
@@ -240,8 +234,8 @@
*/
@Test
public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
- when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_FAILURE_HAL, null));
mWifiStateMachinePrime.enterSoftAPMode(
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
mLooper.dispatchAll();
@@ -255,8 +249,8 @@
*/
@Test
public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
- when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
mWifiStateMachinePrime.enterSoftAPMode(
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
mLooper.dispatchAll();
@@ -330,8 +324,8 @@
*/
@Test
public void testNullApModeConfigFails() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
- when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
mWifiStateMachinePrime.enterSoftAPMode(
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
mLooper.dispatchAll();
@@ -352,7 +346,8 @@
*/
@Test
public void testValidConfigIsSavedOnFailureToStart() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(null);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
WifiConfiguration config = new WifiConfiguration();
config.SSID = "ThisIsAConfig";
@@ -365,14 +360,14 @@
}
/**
- * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config.
+ * Test that two calls to switch to SoftAPMode in succession ends up with the correct config.
*
* Expectation: we should end up in SoftAPMode state configured with the second config.
*/
@Test
public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception {
- when(mWifiInjector.makeWificond()).thenReturn(mWificond);
- when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
+ when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+ .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, mApInterface));
when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
WifiConfiguration config1 = new WifiConfiguration();
@@ -406,12 +401,12 @@
/**
* Test that we safely disable wifi if it is already disabled.
- * Expectations: We should not interact with wificond since we should have already cleaned up
+ * Expectations: We should not interact with WifiNative since we should have already cleaned up
* everything.
*/
@Test
public void disableWifiWhenAlreadyOff() throws Exception {
- verifyNoMoreInteractions(mWificond);
+ verifyNoMoreInteractions(mWifiNative);
mWifiStateMachinePrime.disableWifi();
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 7a7fa92..e6972ea 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -376,6 +376,7 @@
@Mock IProvisioningCallback mProvisioningCallback;
@Mock HandlerThread mWifiServiceHandlerThread;
@Mock WifiPermissionsWrapper mWifiPermissionsWrapper;
+ @Mock WakeupController mWakeupController;
public WifiStateMachineTest() throws Exception {
}
@@ -422,6 +423,7 @@
when(mWifiServiceHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mWifiServiceHandlerThread);
when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+ when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController);
when(mWifiNative.setupForClientMode(WIFI_IFACE_NAME))
.thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mClientInterface));
@@ -2199,6 +2201,16 @@
assertEquals(sBSSID1, wifiInfo.getBSSID());
assertEquals(sFreq1, wifiInfo.getFrequency());
assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.DISCONNECTED));
+ mLooper.dispatchAll();
+
+ wifiInfo = mWsm.getWifiInfo();
+ assertEquals(null, wifiInfo.getBSSID());
+ assertEquals(WifiSsid.NONE, wifiInfo.getSSID());
+ assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId());
+ assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
}
/**
@@ -2819,4 +2831,15 @@
currentConfig.networkId = lastSelectedNetworkId - 1;
assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
}
+
+ /**
+ * Verify that WSM dump includes WakeupController.
+ */
+ @Test
+ public void testDumpShouldDumpWakeupController() {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ PrintWriter writer = new PrintWriter(stream);
+ mWsm.dump(null, writer, null);
+ verify(mWakeupController).dump(null, writer, null);
+ }
}