Merge "WifiBackupRestore: Ignore unkown bitset values from backup" into pi-dev
diff --git a/service/java/com/android/server/wifi/CarrierNetworkConfig.java b/service/java/com/android/server/wifi/CarrierNetworkConfig.java
index efe695b..db107bf 100644
--- a/service/java/com/android/server/wifi/CarrierNetworkConfig.java
+++ b/service/java/com/android/server/wifi/CarrierNetworkConfig.java
@@ -47,6 +47,7 @@
private static final int CONFIG_ELEMENT_SIZE = 2;
private final Map<String, NetworkInfo> mCarrierNetworkMap;
+ private boolean mIsCarrierImsiEncryptionInfoAvailable = false;
public CarrierNetworkConfig(Context context) {
mCarrierNetworkMap = new HashMap<>();
@@ -89,6 +90,40 @@
}
/**
+ * @return True if carrier IMSI encryption info is available, False otherwise.
+ */
+ public boolean isCarrierEncryptionInfoAvailable() {
+ return mIsCarrierImsiEncryptionInfoAvailable;
+ }
+
+ /**
+ * Verify whether carrier IMSI encryption info is available.
+ *
+ * @param context Current application context
+ *
+ * @return True if carrier IMSI encryption info is available, False otherwise.
+ */
+ private boolean verifyCarrierImsiEncryptionInfoIsAvailable(Context context) {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (telephonyManager == null) {
+ return false;
+ }
+ try {
+ ImsiEncryptionInfo imsiEncryptionInfo = telephonyManager
+ .getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN);
+ if (imsiEncryptionInfo == null) {
+ return false;
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Utility class for storing carrier network information.
*/
private static class NetworkInfo {
@@ -108,6 +143,8 @@
* @param context Current application context
*/
private void updateNetworkConfig(Context context) {
+ mIsCarrierImsiEncryptionInfoAvailable = verifyCarrierImsiEncryptionInfoIsAvailable(context);
+
// Reset network map.
mCarrierNetworkMap.clear();
@@ -127,22 +164,6 @@
return;
}
- TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- if (telephonyManager == null) {
- return;
- }
- try {
- ImsiEncryptionInfo imsiEncryptionInfo = telephonyManager
- .getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN);
- if (imsiEncryptionInfo == null) {
- return;
- }
- } catch (RuntimeException e) {
- Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage());
- return;
- }
-
// Process the carrier config for each active subscription.
for (SubscriptionInfo subInfo : subInfoList) {
processNetworkConfig(
diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java
index 7ab33dd..14d55de 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 static final String TAG = "ClientModeManager";
+ private final ClientModeStateMachine mStateMachine;
- ClientModeManager() {
+ private final Context mContext;
+ private final WifiNative mWifiNative;
+
+ private final WifiMetrics mWifiMetrics;
+ private final Listener mListener;
+ private final ScanRequestProxy mScanRequestProxy;
+
+ private String mClientInterfaceName;
+
+
+ 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/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java
index b7f5411..f3cbc95 100644
--- a/service/java/com/android/server/wifi/ScanDetailCache.java
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -19,8 +19,6 @@
import android.annotation.NonNull;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.os.SystemClock;
-import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -169,75 +167,6 @@
return list;
}
- /**
- * Method to get cached scan results that are less than 'age' old.
- *
- * @param age long Time window of desired results.
- * @return WifiConfiguration.Visibility matches in the given visibility
- */
- public WifiConfiguration.Visibility getVisibilityByRssi(long age) {
- WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
-
- long now_ms = System.currentTimeMillis();
- long now_elapsed_ms = SystemClock.elapsedRealtime();
- for (ScanDetail scanDetail : values()) {
- ScanResult result = scanDetail.getScanResult();
- if (scanDetail.getSeen() == 0) {
- continue;
- }
-
- if (result.is5GHz()) {
- //strictly speaking: [4915, 5825]
- //number of known BSSID on 5GHz band
- status.num5 = status.num5 + 1;
- } else if (result.is24GHz()) {
- //strictly speaking: [2412, 2482]
- //number of known BSSID on 2.4Ghz band
- status.num24 = status.num24 + 1;
- }
-
- if (result.timestamp != 0) {
- if (DBG) {
- Log.e("getVisibilityByRssi", " considering " + result.SSID + " " + result.BSSID
- + " elapsed=" + now_elapsed_ms + " timestamp=" + result.timestamp
- + " age = " + age);
- }
- if ((now_elapsed_ms - (result.timestamp / 1000)) > age) continue;
- } else {
- // This checks the time at which we have received the scan result from supplicant
- if ((now_ms - result.seen) > age) continue;
- }
-
- if (result.is5GHz()) {
- if (result.level > status.rssi5) {
- status.rssi5 = result.level;
- status.age5 = result.seen;
- status.BSSID5 = result.BSSID;
- }
- } else if (result.is24GHz()) {
- if (result.level > status.rssi24) {
- status.rssi24 = result.level;
- status.age24 = result.seen;
- status.BSSID24 = result.BSSID;
- }
- }
- }
-
- return status;
- }
-
- /**
- * Method to get scan matches for the desired time window. Returns matches by passpoint time if
- * the WifiConfiguration is passpoint.
- *
- * @param age long desired time for matches.
- * @return WifiConfiguration.Visibility matches in the given visibility
- */
- public WifiConfiguration.Visibility getVisibility(long age) {
- return getVisibilityByRssi(age);
- }
-
-
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java
index 588ea6c..8c4c09b 100644
--- a/service/java/com/android/server/wifi/ScanRequestProxy.java
+++ b/service/java/com/android/server/wifi/ScanRequestProxy.java
@@ -16,6 +16,8 @@
package com.android.server.wifi;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.ScanResult;
@@ -24,8 +26,10 @@
import android.os.Binder;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.util.WifiPermissionsUtil;
import java.util.ArrayList;
@@ -46,18 +50,26 @@
* {@link WifiManager#getScanResults()} is invoked.
* c) Will send out the {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast when new
* scan results are available.
+ * d) Throttle scan requests from non-setting apps:
+ * d.1) For foreground apps, throttle to a max of 1 scan per app every 30 seconds.
+ * d.2) For background apps, throttle to a max of 1 scan from any app every 30 minutes.
* Note: This class is not thread-safe. It needs to be invoked from WifiStateMachine thread only.
- * TODO (b/68987915): Port over scan throttling logic from WifiService for all apps.
- * TODO: Port over idle mode handling from WifiService.
*/
@NotThreadSafe
public class ScanRequestProxy {
private static final String TAG = "WifiScanRequestProxy";
+ @VisibleForTesting
+ public static final int SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS = 30 * 1000;
+ @VisibleForTesting
+ public static final int SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS = 30 * 60 * 1000;
private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final ActivityManager mActivityManager;
private final WifiInjector mWifiInjector;
private final WifiConfigManager mWifiConfigManager;
private final WifiPermissionsUtil mWifiPermissionsUtil;
+ private final Clock mClock;
private WifiScanner mWifiScanner;
// Verbose logging flag.
@@ -66,6 +78,10 @@
private boolean mScanningForHiddenNetworksEnabled = false;
// Flag to indicate that we're waiting for scan results from an existing request.
private boolean mIsScanProcessingComplete = true;
+ // Timestamps for the last scan requested by any background app.
+ private long mLastScanTimestampForBgApps = 0;
+ // Timestamps for the last scan requested by each foreground app.
+ private final ArrayMap<String, Long> mLastScanTimestampsForFgApps = new ArrayMap();
// Scan results cached from the last full single scan request.
private final List<ScanResult> mLastScanResults = new ArrayList<>();
// Common scan listener for scan requests.
@@ -117,12 +133,16 @@
}
};
- ScanRequestProxy(Context context, WifiInjector wifiInjector, WifiConfigManager configManager,
- WifiPermissionsUtil wifiPermissionUtil) {
+ ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager,
+ WifiInjector wifiInjector, WifiConfigManager configManager,
+ WifiPermissionsUtil wifiPermissionUtil, Clock clock) {
mContext = context;
+ mAppOps = appOpsManager;
+ mActivityManager = activityManager;
mWifiInjector = wifiInjector;
mWifiConfigManager = configManager;
mWifiPermissionsUtil = wifiPermissionUtil;
+ mClock = clock;
}
/**
@@ -184,15 +204,114 @@
}
/**
+ * Helper method to send the scan request failure broadcast to specified package.
+ */
+ private void sendScanResultFailureBroadcastToPackage(String packageName) {
+ // clear calling identity to send broadcast
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ intent.setPackage(packageName);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ // restore calling identity
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * Checks if the scan request from the app (specified by packageName) needs
+ * to be throttled.
+ */
+ private boolean shouldScanRequestBeThrottledForForegroundApp(String packageName) {
+ long lastScanMs = mLastScanTimestampsForFgApps.getOrDefault(packageName, 0L);
+ long elapsedRealtime = mClock.getElapsedSinceBootMillis();
+ if (lastScanMs != 0
+ && (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS) {
+ return true;
+ }
+ // Proceed with the scan request and record the time.
+ mLastScanTimestampsForFgApps.put(packageName, elapsedRealtime);
+ return false;
+ }
+
+ /**
+ * Checks if the scan request from a background app needs to be throttled.
+ */
+ private boolean shouldScanRequestBeThrottledForBackgroundApp() {
+ long lastScanMs = mLastScanTimestampForBgApps;
+ long elapsedRealtime = mClock.getElapsedSinceBootMillis();
+ if (lastScanMs != 0
+ && (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS) {
+ return true;
+ }
+ // Proceed with the scan request and record the time.
+ mLastScanTimestampForBgApps = elapsedRealtime;
+ return false;
+ }
+
+ /**
+ * Check if the request comes from background app.
+ */
+ private boolean isRequestFromBackground(int callingUid, String packageName) {
+ mAppOps.checkPackage(callingUid, packageName);
+ // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming
+ // identity so the permission check can be done on system process where wifi runs in.
+ long callingIdentity = Binder.clearCallingIdentity();
+ // TODO(b/74970282): This try/catch block may not be necessary (here & above) because all
+ // of these calls are already in WSM thread context (offloaded from app's binder thread).
+ try {
+ return mActivityManager.getPackageImportance(packageName)
+ > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * Checks if the scan request from the app (specified by callingUid & packageName) needs
+ * to be throttled.
+ *
+ * a) Each foreground app can request 1 scan every
+ * {@link #SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS}.
+ * b) Background apps combined can request 1 scan every
+ * {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}.
+ */
+ private boolean shouldScanRequestBeThrottledForApp(int callingUid, String packageName) {
+ boolean isThrottled;
+ if (isRequestFromBackground(callingUid, packageName)) {
+ isThrottled = shouldScanRequestBeThrottledForBackgroundApp();
+ if (isThrottled && mVerboseLoggingEnabled) {
+ Log.v(TAG, "Background scan app request [" + callingUid + ", " + packageName + "]");
+ }
+ } else {
+ isThrottled = shouldScanRequestBeThrottledForForegroundApp(packageName);
+ if (isThrottled && mVerboseLoggingEnabled) {
+ Log.v(TAG, "Foreground scan app request [" + callingUid + ", " + packageName + "]");
+ }
+ }
+ return isThrottled;
+ }
+
+ /**
* Initiate a wifi scan.
*
* @param callingUid The uid initiating the wifi scan. Blame will be given to this uid.
* @return true if the scan request was placed or a scan is already ongoing, false otherwise.
*/
- public boolean startScan(int callingUid) {
+ public boolean startScan(int callingUid, String packageName) {
if (!retrieveWifiScannerIfNecessary()) {
Log.e(TAG, "Failed to retrieve wifiscanner");
- sendScanResultBroadcast(false);
+ sendScanResultFailureBroadcastToPackage(packageName);
+ return false;
+ }
+ boolean fromSettings = mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid);
+ // Check and throttle scan request from apps without NETWORK_SETTINGS permission.
+ if (!fromSettings && shouldScanRequestBeThrottledForApp(callingUid, packageName)) {
+ Log.i(TAG, "Scan request from " + packageName + " throttled");
+ sendScanResultFailureBroadcastToPackage(packageName);
return false;
}
// Create a worksource using the caller's UID.
@@ -201,7 +320,7 @@
// Create the scan settings.
WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
// Scan requests from apps with network settings will be of high accuracy type.
- if (mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) {
+ if (fromSettings) {
settings.type = WifiScanner.TYPE_HIGH_ACCURACY;
}
// always do full scans
@@ -234,5 +353,7 @@
*/
public void clearScanResults() {
mLastScanResults.clear();
+ mLastScanTimestampForBgApps = 0;
+ mLastScanTimestampsForFgApps.clear();
}
}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 7eeab4d..6594945 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -277,8 +277,11 @@
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mOpenNetworkNotifier.handleScanResults(
mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
- mCarrierNetworkNotifier.handleScanResults(mNetworkSelector
- .getFilteredScanDetailsForCarrierUnsavedNetworks(mCarrierNetworkConfig));
+ if (mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) {
+ mCarrierNetworkNotifier.handleScanResults(
+ mNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks(
+ mCarrierNetworkConfig));
+ }
}
return false;
}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 28e3616..b6beb68 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.content.Context;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
@@ -237,8 +238,11 @@
mPasspointNetworkEvaluator = new PasspointNetworkEvaluator(
mPasspointManager, mWifiConfigManager, mConnectivityLocalLog);
mWifiMetrics.setPasspointManager(mPasspointManager);
- mScanRequestProxy = new ScanRequestProxy(mContext, this, mWifiConfigManager,
- mWifiPermissionsUtil);
+ mScanRequestProxy = new ScanRequestProxy(mContext,
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
+ (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE),
+ this, mWifiConfigManager,
+ mWifiPermissionsUtil, mClock);
// mWifiStateMachine has an implicit dependency on mJavaRuntime due to WifiDiagnostics.
mJavaRuntime = Runtime.getRuntime();
mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade,
@@ -450,6 +454,17 @@
}
/**
+ * 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/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index d89d6e8..99ae01f 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -452,15 +452,15 @@
}
}
- /** Helper method invoked to cleanup state after one of the native daemon's death. */
+ /**
+ * Helper method invoked to trigger the status changed callback after one of the native
+ * daemon's death.
+ */
private void onNativeDaemonDeath() {
synchronized (mLock) {
- Log.i(TAG, "One of the daemons died. Tearing down everything");
- teardownAllInterfaces();
for (StatusListener listener : mStatusListeners) {
listener.onStatusChanged(false);
}
- // TODO(70572148): Do we need to wait to mark the system ready again?
for (StatusListener listener : mStatusListeners) {
listener.onStatusChanged(true);
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 9d292e0..a88b20d 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -92,8 +92,6 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import android.util.MutableInt;
import android.util.Slog;
@@ -159,14 +157,12 @@
private final Context mContext;
private final FrameworkFacade mFacade;
private final Clock mClock;
- private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
private final PowerManager mPowerManager;
private final AppOpsManager mAppOps;
private final UserManager mUserManager;
private final ActivityManager mActivityManager;
private final WifiCountryCode mCountryCode;
- private long mBackgroundThrottleInterval;
// Debug counter tracking scan requests sent by WifiManager
private int scanRequestCounter = 0;
@@ -181,9 +177,6 @@
/* Backup/Restore Module */
private final WifiBackupRestore mWifiBackupRestore;
- // Map of package name of background scan apps and last scan timestamp.
- private final ArrayMap<String, Long> mLastScanTimestamps;
-
private WifiLog mLog;
/**
@@ -482,9 +475,6 @@
mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
mLog = mWifiInjector.makeLog(TAG);
mFrameworkFacade = wifiInjector.getFrameworkFacade();
- mLastScanTimestamps = new ArrayMap<>();
- updateBackgroundThrottleInterval();
- updateBackgroundThrottlingWhitelist();
mIfaceIpModes = new ConcurrentHashMap<>();
mLocalOnlyHotspotRequests = new HashMap<>();
enableVerboseLoggingInternal(getVerboseLoggingLevel());
@@ -524,7 +514,6 @@
(wifiEnabled ? "enabled" : "disabled"));
registerForScanModeChange();
- registerForBackgroundThrottleChanges();
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -629,18 +618,6 @@
int callingUid = Binder.getCallingUid();
mLog.info("startScan uid=%").c(callingUid).flush();
- // Check and throttle background apps for wifi scan.
- if (isRequestFromBackground(packageName)) {
- long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
- long elapsedRealtime = mClock.getElapsedSinceBootMillis();
-
- if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
- sendFailedScanBroadcast();
- return;
- }
- // Proceed with the scan request and record the time.
- mLastScanTimestamps.put(packageName, elapsedRealtime);
- }
synchronized (this) {
if (mInIdleMode) {
// Need to send an immediate scan result broadcast in case the
@@ -656,7 +633,7 @@
}
}
boolean success = mWifiInjector.getWifiStateMachineHandler().runWithScissors(() -> {
- if (!mScanRequestProxy.startScan(callingUid)) {
+ if (!mScanRequestProxy.startScan(callingUid, packageName)) {
Log.e(TAG, "Failed to start scan");
}
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
@@ -683,29 +660,6 @@
}
- // Check if the request comes from background.
- private boolean isRequestFromBackground(String packageName) {
- // Requests from system or wifi are not background.
- if (Binder.getCallingUid() == Process.SYSTEM_UID
- || Binder.getCallingUid() == Process.WIFI_UID) {
- return false;
- }
- mAppOps.checkPackage(Binder.getCallingUid(), packageName);
- if (mBackgroundThrottlePackageWhitelist.contains(packageName)) {
- return false;
- }
-
- // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming
- // identify so the permission check can be done on system process where wifi runs in.
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- return mActivityManager.getPackageImportance(packageName)
- > BACKGROUND_IMPORTANCE_CUTOFF;
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
/**
* WPS support in Client mode is deprecated. Return null.
*/
@@ -2414,51 +2368,6 @@
}
- // Monitors settings changes related to background wifi scan throttling.
- private void registerForBackgroundThrottleChanges() {
- mFrameworkFacade.registerContentObserver(
- mContext,
- Settings.Global.getUriFor(
- Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
- false,
- new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- updateBackgroundThrottleInterval();
- }
- }
- );
- mFrameworkFacade.registerContentObserver(
- mContext,
- Settings.Global.getUriFor(
- Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
- false,
- new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- updateBackgroundThrottlingWhitelist();
- }
- }
- );
- }
-
- private void updateBackgroundThrottleInterval() {
- mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting(
- mContext,
- Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
- DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
- }
-
- private void updateBackgroundThrottlingWhitelist() {
- String setting = mFrameworkFacade.getStringSetting(
- mContext,
- Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
- mBackgroundThrottlePackageWhitelist.clear();
- if (setting != null) {
- mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
- }
- }
-
private void registerForBroadcasts() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index e690ef9..bdc1a60 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -2226,9 +2226,6 @@
config = mWifiConfigManager.getConfiguredNetwork(msg.arg1);
if (config != null) {
sb.append(" ").append(config.configKey());
- if (config.visibility != null) {
- sb.append(" ").append(config.visibility.toString());
- }
}
if (mTargetRoamBSSID != null) {
sb.append(" ").append(mTargetRoamBSSID);
@@ -2237,9 +2234,6 @@
config = getCurrentWifiConfiguration();
if (config != null) {
sb.append(config.configKey());
- if (config.visibility != null) {
- sb.append(" ").append(config.visibility.toString());
- }
}
break;
case CMD_START_ROAM:
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
index 6baf5ad..8eb521c 100644
--- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -80,6 +80,17 @@
// 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 @@
}
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);
diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
index b06c8a4..e8eb6ce 100644
--- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
@@ -124,21 +124,32 @@
}
/**
- * Verify that {@link CarrierNetworkConfig#isCarrierNetwork} will return false when the given
- * SSID is associated with a carrier network, but IMSI encryption info is not available.
+ * Verify that {@link CarrierNetworkConfig#isCarrierEncryptionInfoAvailable} will return true
+ * when the carrier IMSI encryption info is available.
*
* @throws Exception
*/
@Test
- public void getExistingCarrierNetworkInfoWhenEncryptionInfoNotAvailable() throws Exception {
- when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID))
- .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE));
+ public void verifyIsCarrierEncryptionInfoAvailableReturnsTrueWhenEncryptionInfoIsAvailable()
+ throws Exception {
+ assertTrue(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable());
+ }
+
+ /**
+ * Verify that {@link CarrierNetworkConfig#isCarrierEncryptionInfoAvailable} will return false
+ * when the carrier IMSI encryption info is not available.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyIsCarrierEncryptionInfoAvailableReturnsFalseWhenEncryptionInfoNotAvailable()
+ throws Exception {
when(mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN))
.thenReturn(null);
mBroadcastReceiver.onReceive(mContext,
new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
- assertFalse(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID));
+ assertFalse(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable());
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java
new file mode 100644
index 0000000..942ff1c
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2018 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 static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_STATE;
+import static android.net.wifi.WifiManager.EXTRA_SCAN_AVAILABLE;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
+import static android.net.wifi.WifiManager.WIFI_SCAN_AVAILABLE;
+import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Unit tests for {@link ClientModeManager}.
+ */
+@SmallTest
+public class ClientModeManagerTest {
+ private static final String TAG = "ClientModeManagerTest";
+ private static final String TEST_INTERFACE_NAME = "testif0";
+ private static final String OTHER_INTERFACE_NAME = "notTestIf";
+
+ TestLooper mLooper;
+
+ ClientModeManager mClientModeManager;
+
+ @Mock Context mContext;
+ @Mock WifiMetrics mWifiMetrics;
+ @Mock WifiNative mWifiNative;
+ @Mock ClientModeManager.Listener mListener;
+ @Mock WifiMonitor mWifiMonitor;
+ @Mock ScanRequestProxy mScanRequestProxy;
+
+ final ArgumentCaptor<WifiNative.StatusListener> mStatusListenerCaptor =
+ ArgumentCaptor.forClass(WifiNative.StatusListener.class);
+ final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor =
+ ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+
+ mClientModeManager = createClientModeManager();
+ mLooper.dispatchAll();
+ }
+
+ private ClientModeManager createClientModeManager() {
+ return new ClientModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener,
+ mWifiMetrics, mScanRequestProxy);
+ }
+
+ private void startClientModeAndVerifyEnabled() throws Exception {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ when(mWifiNative.setupInterfaceForClientMode(eq(false), any()))
+ .thenReturn(TEST_INTERFACE_NAME);
+ mClientModeManager.start();
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).registerStatusListener(mStatusListenerCaptor.capture());
+ verify(mWifiNative).setupInterfaceForClientMode(
+ eq(false), mInterfaceCallbackCaptor.capture());
+
+ // now mark the interface as up
+ mInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME);
+ mLooper.dispatchAll();
+
+ verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(),
+ eq(UserHandle.ALL));
+
+ List<Intent> intents = intentCaptor.getAllValues();
+ assertEquals(3, intents.size());
+ Log.d(TAG, "captured intents: " + intents);
+ checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, WIFI_STATE_DISABLED);
+ checkWifiScanStateChangedBroadcast(intents.get(1), WIFI_STATE_ENABLED);
+ checkWifiStateChangedBroadcast(intents.get(2), WIFI_STATE_ENABLED, WIFI_STATE_ENABLING);
+
+ checkWifiStateChangeListenerUpdate(WIFI_STATE_ENABLED);
+ verify(mScanRequestProxy, atLeastOnce()).enableScanningForHiddenNetworks(true);
+ }
+
+ private void checkWifiScanStateChangedBroadcast(Intent intent, int expectedCurrentState) {
+ String action = intent.getAction();
+ assertEquals(WIFI_SCAN_AVAILABLE, action);
+ int currentState = intent.getIntExtra(EXTRA_SCAN_AVAILABLE, WIFI_STATE_UNKNOWN);
+ assertEquals(expectedCurrentState, currentState);
+ }
+
+ private void checkWifiStateChangedBroadcast(
+ Intent intent, int expectedCurrentState, int expectedPrevState) {
+ String action = intent.getAction();
+ assertEquals(WIFI_STATE_CHANGED_ACTION, action);
+ int currentState = intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_UNKNOWN);
+ assertEquals(expectedCurrentState, currentState);
+ int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_STATE, WIFI_STATE_UNKNOWN);
+ assertEquals(expectedPrevState, prevState);
+ }
+
+
+ private void checkWifiStateChangeListenerUpdate(int expectedCurrentState) {
+ verify(mListener).onStateChanged(eq(expectedCurrentState));
+ }
+
+ private void verifyNotificationsForCleanShutdown(int fromState) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeastOnce())
+ .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL));
+
+ List<Intent> intents = intentCaptor.getAllValues();
+ assertEquals(3, intents.size());
+ checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLING, fromState);
+ checkWifiScanStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED);
+ checkWifiStateChangedBroadcast(intents.get(2), WIFI_STATE_DISABLED, WIFI_STATE_DISABLING);
+ verify(mScanRequestProxy).enableScanningForHiddenNetworks(false);
+ verify(mScanRequestProxy).clearScanResults();
+ }
+
+ private void verifyNotificationsForFailure() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeastOnce())
+ .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL));
+
+ List<Intent> intents = intentCaptor.getAllValues();
+ assertEquals(3, intents.size());
+ checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLING, WIFI_STATE_ENABLED);
+ checkWifiScanStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED);
+ checkWifiStateChangedBroadcast(intents.get(2), WIFI_STATE_DISABLED, WIFI_STATE_DISABLING);
+ checkWifiStateChangeListenerUpdate(WIFI_STATE_DISABLED);
+ verify(mScanRequestProxy).enableScanningForHiddenNetworks(false);
+ verify(mScanRequestProxy).clearScanResults();
+ }
+
+ /**
+ * ClientMode start sets up an interface in ClientMode.
+ */
+ @Test
+ public void clientModeStartCreatesClientInterface() throws Exception {
+ startClientModeAndVerifyEnabled();
+ }
+
+ /**
+ * ClientMode increments failure metrics when failing to setup client mode.
+ */
+ @Test
+ public void detectAndReportErrorWhenSetupForClientWifiNativeFailure() throws Exception {
+ when(mWifiNative.setupInterfaceForClientMode(eq(false), any())).thenReturn(null);
+ mClientModeManager.start();
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(),
+ eq(UserHandle.ALL));
+ checkWifiScanStateChangedBroadcast(intentCaptor.getValue(), WIFI_STATE_DISABLED);
+ checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN);
+ }
+
+ /**
+ * ClientMode start does not indicate scanning is available when the interface name is empty.
+ */
+ @Test
+ public void clientModeStartDoesNotSendScanningActiveWhenClientInterfaceNameIsEmpty()
+ throws Exception {
+ when(mWifiNative.setupInterfaceForClientMode(eq(false), any())).thenReturn("");
+ mClientModeManager.start();
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(),
+ eq(UserHandle.ALL));
+
+ List<Intent> intents = intentCaptor.getAllValues();
+ assertEquals(2, intents.size());
+ checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, WIFI_STATE_DISABLED);
+ checkWifiScanStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED);
+ checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN);
+ }
+
+ /**
+ * Calling ClientModeManager.start twice does not crash or restart client mode.
+ */
+ @Test
+ public void clientModeStartCalledTwice() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mWifiNative, mContext);
+ mClientModeManager.start();
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mWifiNative, mContext);
+ }
+
+ /**
+ * ClientMode stop properly cleans up state
+ */
+ @Test
+ public void clientModeStopCleansUpState() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext);
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+
+ verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+ }
+
+ /**
+ * Calling stop when ClientMode is not started should not send scan state updates
+ */
+ @Test
+ public void clientModeStopWhenNotStartedDoesNotUpdateScanStateUpdates() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext);
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ reset(mContext, mListener);
+ // now call stop again
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
+ verify(mListener, never()).onStateChanged(anyInt());
+ }
+
+ /**
+ * Triggering interface down when ClientMode is active properly exits the active state.
+ */
+ @Test
+ public void clientModeStartedStopsWhenInterfaceDown() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext, mScanRequestProxy);
+ mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME);
+ mLooper.dispatchAll();
+ verifyNotificationsForFailure();
+ }
+
+ /**
+ * Testing the handling of a wifinative failure status change notification.
+ */
+ @Test
+ public void clientModeStartedStopsOnNativeFailure() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext, mScanRequestProxy, mListener);
+ mStatusListenerCaptor.getValue().onStatusChanged(false);
+ mLooper.dispatchNext();
+
+ checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN);
+
+ mLooper.dispatchAll();
+
+ verifyNotificationsForFailure();
+ }
+
+ /**
+ * Testing that handling of a wifinative callback that is not a failuer does not stop client
+ * mode.
+ */
+ @Test
+ public void clientModeStartedAndStaysUpOnNativeNonFailureCallback() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext, mScanRequestProxy, mListener);
+ mStatusListenerCaptor.getValue().onStatusChanged(true);
+ mLooper.dispatchAll();
+
+ verify(mListener, never()).onStateChanged(eq(WIFI_STATE_UNKNOWN));
+ verify(mListener, never()).onStateChanged(eq(WIFI_STATE_DISABLING));
+ verify(mListener, never()).onStateChanged(eq(WIFI_STATE_DISABLED));
+ }
+
+
+ /**
+ * Testing the handling of an interface destroyed notification.
+ */
+ @Test
+ public void clientModeStartedStopsOnInterfaceDestroyed() throws Exception {
+ startClientModeAndVerifyEnabled();
+ reset(mContext, mScanRequestProxy, mListener);
+
+ mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME);
+ mLooper.dispatchAll();
+ verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
index 759fbe2..7f684bc 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
@@ -16,9 +16,13 @@
package com.android.server.wifi;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.ScanResult;
@@ -47,6 +51,8 @@
@SmallTest
public class ScanRequestProxyTest {
private static final int TEST_UID = 5;
+ private static final String TEST_PACKAGE_NAME_1 = "com.test.1";
+ private static final String TEST_PACKAGE_NAME_2 = "com.test.2";
private static final List<WifiScanner.ScanSettings.HiddenNetwork> TEST_HIDDEN_NETWORKS_LIST =
new ArrayList<WifiScanner.ScanSettings.HiddenNetwork>() {{
add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_1"));
@@ -55,10 +61,13 @@
}};
@Mock private Context mContext;
+ @Mock private AppOpsManager mAppOps;
+ @Mock private ActivityManager mActivityManager;
@Mock private WifiInjector mWifiInjector;
@Mock private WifiConfigManager mWifiConfigManager;
@Mock private WifiScanner mWifiScanner;
@Mock private WifiPermissionsUtil mWifiPermissionsUtil;
+ @Mock private Clock mClock;
private ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor =
ArgumentCaptor.forClass(WorkSource.class);
private ArgumentCaptor<WifiScanner.ScanSettings> mScanSettingsArgumentCaptor =
@@ -87,7 +96,8 @@
mTestScanDatas2 = ScanTestUtil.createScanDatas(new int[][]{ { 2412, 2422, 5200, 5210 } });
mScanRequestProxy =
- new ScanRequestProxy(mContext, mWifiInjector, mWifiConfigManager, mWifiPermissionsUtil);
+ new ScanRequestProxy(mContext, mAppOps, mActivityManager, mWifiInjector,
+ mWifiConfigManager, mWifiPermissionsUtil, mClock);
}
@After
@@ -101,8 +111,8 @@
@Test
public void testStartScanFailWithoutScanner() {
when(mWifiInjector.getWifiScanner()).thenReturn(null);
- assertFalse(mScanRequestProxy.startScan(TEST_UID));
- validateScanResultsAvailableBroadcastSent(false);
+ assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1);
verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
}
@@ -112,7 +122,7 @@
*/
@Test
public void testStartScanSuccess() {
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID)));
@@ -127,7 +137,7 @@
@Test
public void testStartScanSuccessFromAppWithNetworkSettings() {
when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true);
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID)));
@@ -143,7 +153,7 @@
@Test
public void testStartScanWithHiddenNetworkScanningDisabled() {
mScanRequestProxy.enableScanningForHiddenNetworks(false);
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiConfigManager, never()).retrieveHiddenNetworkList();
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
@@ -159,7 +169,7 @@
@Test
public void testStartScanWithHiddenNetworkScanningEnabled() {
mScanRequestProxy.enableScanningForHiddenNetworks(true);
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiConfigManager).retrieveHiddenNetworkList();
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
@@ -213,7 +223,7 @@
@Test
public void testScanSuccessOverwritesPreviousResults() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Verify the scan results processing for request 1.
mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
@@ -224,7 +234,7 @@
mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Verify the scan results processing for request 2.
mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas2);
@@ -243,7 +253,7 @@
@Test
public void testScanFailureDoesNotOverwritePreviousResults() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Verify the scan results processing for request 1.
mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
@@ -254,7 +264,7 @@
mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Verify the scan failure processing.
mScanListenerArgumentCaptor.getValue().onFailure(0, "failed");
@@ -277,12 +287,12 @@
WifiScanner.ScanListener listener1;
WifiScanner.ScanListener listener2;
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
listener1 = mScanListenerArgumentCaptor.getValue();
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
// Ensure that we did send a second scan request to scanner.
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
listener2 = mScanListenerArgumentCaptor.getValue();
@@ -314,7 +324,7 @@
@Test
public void testNewScanRequestAfterPreviousScanSucceeds() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send the scan results for request 1.
mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
@@ -325,7 +335,7 @@
mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
// Ensure that we did send a second scan request to scanner.
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send the scan results for request 2.
@@ -347,7 +357,7 @@
@Test
public void testNewScanRequestAfterPreviousScanSucceedsWithInvalidScanDatas() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send scan success for request 1, but with invalid scan datas.
@@ -358,7 +368,7 @@
assertTrue(mScanRequestProxy.getScanResults().isEmpty());
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
// Ensure that we did send a second scan request to scanner.
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send the scan results for request 2.
@@ -380,7 +390,7 @@
@Test
public void testNewScanRequestAfterPreviousScanFailure() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send scan failure for request 1.
@@ -390,7 +400,7 @@
assertTrue(mScanRequestProxy.getScanResults().isEmpty());
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
// Ensure that we did send a second scan request to scanner.
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Now send the scan results for request 2.
@@ -410,7 +420,7 @@
@Test
public void testClearScanResults() {
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
// Verify the scan results processing for request 1.
mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
@@ -433,12 +443,12 @@
WifiScanner.ScanListener listener1;
WifiScanner.ScanListener listener2;
// Make scan request 1.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
listener1 = mScanListenerArgumentCaptor.getValue();
// Make scan request 2.
- assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
// Ensure that we did send a second scan request to scanner.
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
listener2 = mScanListenerArgumentCaptor.getValue();
@@ -448,6 +458,147 @@
verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
}
+ /**
+ * Ensure new scan requests from the same app are rejected if it's before
+ * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS}
+ */
+ @Test
+ public void testSuccessiveScanRequestFromSameAppThrottled() {
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ long secondRequestMs =
+ firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS - 1;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs);
+ // Make scan request 2 from the same package name & ensure that it is throttled.
+ assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Ensure new scan requests from the same app are not rejected if it's after
+ * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS}
+ */
+ @Test
+ public void testSuccessiveScanRequestFromSameAppNotThrottled() {
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ long secondRequestMs =
+ firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS + 1;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs);
+ // Make scan request 2 from the same package name & ensure that it is not throttled.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Ensure new scan requests from the same app with NETWORK_SETTINGS permission are not
+ * throttled.
+ */
+ @Test
+ public void testSuccessiveScanRequestFromSameAppWithNetworkSettingsPermissionNotThrottled() {
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true);
+
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ long secondRequestMs =
+ firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_FG_APPS_MS - 1;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs);
+ // Make scan request 2 from the same package name & ensure that it is not throttled.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Ensure new scan requests from different apps are not throttled.
+ */
+ @Test
+ public void testSuccessiveScanRequestFromDifferentAppsNotThrottled() {
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ // Make scan request 2 from the same package name & ensure that it is throttled.
+ assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1);
+
+ // Make scan request 3 from a different package name & ensure that it is not throttled.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Ensure scan requests from different background apps are throttled if it's before
+ * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}.
+ */
+ @Test
+ public void testSuccessiveScanRequestFromBgAppsThrottled() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE + 1);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE + 1);
+
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ // Make scan request 2 from the different package name & ensure that it is throttled.
+ assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
+ validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_2);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Ensure scan requests from different background apps are not throttled if it's after
+ * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}.
+ */
+ @Test
+ public void testSuccessiveScanRequestFromBgAppsNotThrottled() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + 1);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + 1);
+
+ long firstRequestMs = 782;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs);
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ long secondRequestMs =
+ firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS + 1;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs);
+ // Make scan request 2 from the different package name & ensure that it is throttled.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
private void validateScanSettings(WifiScanner.ScanSettings scanSettings,
boolean expectHiddenNetworks) {
validateScanSettings(scanSettings, expectHiddenNetworks, false);
@@ -502,4 +653,21 @@
boolean scanSucceeded = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
assertEquals(expectScanSuceeded, scanSucceeded);
}
+
+ private void validateScanResultsFailureBroadcastSent(String expectedPackageName) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(UserHandle.class);
+ mInOrder.verify(mContext).sendBroadcastAsUser(
+ intentCaptor.capture(), userHandleCaptor.capture());
+
+ assertEquals(userHandleCaptor.getValue(), UserHandle.ALL);
+
+ Intent intent = intentCaptor.getValue();
+ assertEquals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION, intent.getAction());
+ assertEquals(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, intent.getFlags());
+ boolean scanSucceeded = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ assertFalse(scanSucceeded);
+ String packageName = intent.getPackage();
+ assertEquals(expectedPackageName, packageName);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 490d34b..8b6c2fb 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -97,6 +97,7 @@
R.integer.config_wifi_framework_max_tx_rate_for_full_scan);
mFullScanMaxRxPacketRate = mResource.getInteger(
R.integer.config_wifi_framework_max_rx_rate_for_full_scan);
+ when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
}
/**
@@ -764,6 +765,36 @@
}
/**
+ * {@link CarrierNetworkNotifier} does not handle scan results on network selection if carrier
+ * encryption info is not available.
+ *
+ * Expected behavior: CarrierNetworkNotifier does not handle scan results
+ */
+ @Test
+ public void whenNoEncryptionInfoAvailable_CarrierNetworkNotifierDoesNotHandleScanResults() {
+ // no connection candidate selected
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(null);
+
+ List<ScanDetail> expectedCarrierNetworks = new ArrayList<>();
+ expectedCarrierNetworks.add(
+ new ScanDetail(
+ new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
+ CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "[EAP][ESS]", -78, 2450,
+ 1025, 22, 33, 20, 0, 0, true), null));
+
+ when(mWifiNS.getFilteredScanDetailsForCarrierUnsavedNetworks(any()))
+ .thenReturn(expectedCarrierNetworks);
+ when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(false);
+
+ // Set WiFi to disconnected state to trigger PNO scan
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mCarrierNetworkNotifier, never()).handleScanResults(expectedCarrierNetworks);
+ }
+
+ /**
* When wifi is connected, {@link CarrierNetworkNotifier} handles the Wi-Fi connected behavior.
*
* Expected behavior: CarrierNetworkNotifier handles connected behavior
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
index 2fe4452..56ba66a 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
@@ -618,8 +618,6 @@
// Trigger wificond death
mWificondDeathHandlerCaptor.getValue().onDeath();
- validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
- mNetworkObserverCaptor0.getValue());
mInOrder.verify(mWifiMetrics).incrementNumWificondCrashes();
verify(mStatusListener).onStatusChanged(false);
@@ -640,8 +638,6 @@
// Trigger vendor HAL death
mWifiVendorHalDeathHandlerCaptor.getValue().onDeath();
- validateOnDestroyedSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
- mNetworkObserverCaptor0.getValue());
mInOrder.verify(mWifiMetrics).incrementNumHalCrashes();
verify(mStatusListener).onStatusChanged(false);
@@ -661,8 +657,6 @@
// Trigger wificond death
mSupplicantDeathHandlerCaptor.getValue().onDeath();
- validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
- mNetworkObserverCaptor0.getValue());
mInOrder.verify(mWifiMetrics).incrementNumSupplicantCrashes();
verify(mStatusListener).onStatusChanged(false);
@@ -694,8 +688,6 @@
// Trigger vendor HAL death
mHostapdDeathHandlerCaptor.getValue().onDeath();
- validateOnDestroyedSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
- mNetworkObserverCaptor0.getValue());
mInOrder.verify(mWifiMetrics).incrementNumHostapdCrashes();
verify(mStatusListener).onStatusChanged(false);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index 6f99f55..efd9b9f 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -89,7 +89,6 @@
import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
-import android.provider.Settings;
import android.support.test.filters.SmallTest;
import com.android.internal.os.PowerProfile;
@@ -126,9 +125,7 @@
private static final String TAG = "WifiServiceImplTest";
private static final String SCAN_PACKAGE_NAME = "scanPackage";
- private static final String WHITE_LIST_SCAN_PACKAGE_NAME = "whiteListScanPackage";
private static final int DEFAULT_VERBOSE_LOGGING = 0;
- private static final long WIFI_BACKGROUND_SCAN_INTERVAL = 10000;
private static final String ANDROID_SYSTEM_PACKAGE = "android";
private static final String TEST_PACKAGE_NAME = "TestPackage";
private static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
@@ -286,15 +283,6 @@
anyBoolean(), any());
when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
- when(mFrameworkFacade.getLongSetting(
- eq(mContext),
- eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
- anyLong()))
- .thenReturn(WIFI_BACKGROUND_SCAN_INTERVAL);
- when(mFrameworkFacade.getStringSetting(
- eq(mContext),
- eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST)))
- .thenReturn(WHITE_LIST_SCAN_PACKAGE_NAME);
IPowerManager powerManagerService = mock(IPowerManager.class);
mPowerManager = new PowerManager(mContext, powerManagerService, new Handler());
when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
@@ -1029,80 +1017,6 @@
}
/**
- * Ensure foreground apps can always do wifi scans.
- */
- @Test
- public void testWifiScanStartedForeground() {
- setupWifiStateMachineHandlerForRunWithScissors();
-
- when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
- mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy).startScan(Process.myUid());
- verifyCheckChangePermission(SCAN_PACKAGE_NAME);
- }
-
- /**
- * Ensure background apps get throttled when the previous scan is too close.
- */
- @Test
- public void testWifiScanBackgroundThrottled() {
- setupWifiStateMachineHandlerForRunWithScissors();
-
- when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
- long startMs = 1000;
- when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
- mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy).startScan(Process.myUid());
-
- when(mClock.getElapsedSinceBootMillis()).thenReturn(
- startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
- mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy, times(1)).startScan(Process.myUid());
- }
-
- /**
- * Ensure background apps can do wifi scan when the throttle interval reached.
- */
- @Test
- public void testWifiScanBackgroundNotThrottled() {
- setupWifiStateMachineHandlerForRunWithScissors();
-
- when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
- long startMs = 1000;
- when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
- mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy).startScan(Process.myUid());
-
- when(mClock.getElapsedSinceBootMillis()).thenReturn(
- startMs + WIFI_BACKGROUND_SCAN_INTERVAL + 1000);
- mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy, times(2)).startScan(Process.myUid());
- }
-
- /**
- * Ensure background apps can do wifi scan when the throttle interval reached.
- */
- @Test
- public void testWifiScanBackgroundWhiteListed() {
- setupWifiStateMachineHandlerForRunWithScissors();
-
- when(mActivityManager.getPackageImportance(WHITE_LIST_SCAN_PACKAGE_NAME)).thenReturn(
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
- long startMs = 1000;
- when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
- mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy).startScan(Process.myUid());
-
- when(mClock.getElapsedSinceBootMillis()).thenReturn(
- startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
- mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy, times(2)).startScan(Process.myUid());
- }
-
- /**
* Ensure that we handle scan request failure when posting the runnable to handler fails.
*/
@Ignore
@@ -1111,11 +1025,8 @@
setupWifiStateMachineHandlerForRunWithScissors();
doReturn(false).when(mHandlerSpyForWsmRunWithScissors)
.runWithScissors(any(), anyLong());
-
- when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy, never()).startScan(Process.myUid());
+ verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME);
}
static final String TEST_SSID = "Sid's Place";
@@ -2614,14 +2525,14 @@
// Send a scan request while the device is idle.
mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
// No scans must be made yet as the device is idle.
- verify(mScanRequestProxy, never()).startScan(Process.myUid());
+ verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME);
// Tell the wifi service that idle mode ended.
when(mPowerManager.isDeviceIdleMode()).thenReturn(false);
TestUtil.sendIdleModeChanged(mBroadcastReceiverCaptor.getValue(), mContext);
// Must scan now.
- verify(mScanRequestProxy, times(1)).startScan(Process.myUid());
+ verify(mScanRequestProxy).startScan(Process.myUid(), TEST_PACKAGE_NAME);
// The app ops check is executed with this package's identity (not the identity of the
// original remote caller who requested the scan while idle).
verify(mAppOpsManager).noteOp(
@@ -2630,7 +2541,7 @@
// Send another scan request. The device is not idle anymore, so it must be executed
// immediately.
mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
- verify(mScanRequestProxy, times(2)).startScan(Process.myUid());
+ verify(mScanRequestProxy).startScan(Process.myUid(), SCAN_PACKAGE_NAME);
}
private class IdleModeIntentMatcher implements ArgumentMatcher<IntentFilter> {