summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoshan Pius <rpius@google.com>2018-11-06 17:43:02 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2018-11-06 17:43:02 +0000
commitd2a20c1a3975668bc5aeab53a8f0daae6662366a (patch)
tree37b5ec26895032b06815f6b78c2abde4c8a5eea6
parent894960003ef7ccb181dee54da59fe787f471e406 (diff)
parent3ff9e5cb3fe2fab47365bdf3ffb75139c099b3da (diff)
Merge changes from topics "network_request_match_callback", "wifi_network_specifier"
* changes: WifiNetworkFactory: Implement network matching using network specifier ScanResultMatchInfo: Refactor network type retrieval WifiServiceImpl: Network request match callback registration WifiNetworkFactory: Always allow requests from signature app WifiNetworkFactory: Trigger periodic scans WifiConfigurationUtil: Validation for network specifier WifiNetworkFactory: Implement |acceptRequest|
-rw-r--r--service/java/com/android/server/wifi/ClientModeImpl.java26
-rw-r--r--service/java/com/android/server/wifi/ScanResultMatchInfo.java69
-rw-r--r--service/java/com/android/server/wifi/WifiConfigurationUtil.java195
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java6
-rw-r--r--service/java/com/android/server/wifi/WifiNetworkFactory.java489
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java61
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java17
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java117
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java779
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java81
10 files changed, 1780 insertions, 60 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index dd852945b..b401515d5 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -38,6 +38,7 @@ import android.net.IpConfiguration;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
@@ -50,6 +51,7 @@ import android.net.StaticIpConfiguration;
import android.net.TrafficStats;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpClient;
+import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.RssiPacketCountInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
@@ -793,15 +795,18 @@ public class ClientModeImpl extends StateMachine {
// TODO - needs to be a bit more dynamic
mDfltNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
+ NetworkCapabilities factoryNetworkCapabilities =
+ new NetworkCapabilities(mNetworkCapabilitiesFilter);
+ factoryNetworkCapabilities.setNetworkSpecifier(new MatchAllNetworkSpecifier());
// Make the network factories.
mNetworkFactory = mWifiInjector.makeWifiNetworkFactory(
- mNetworkCapabilitiesFilter, mWifiConnectivityManager);
+ factoryNetworkCapabilities, mWifiConnectivityManager);
// We can't filter untrusted network in the capabilities filter because a trusted
// network would still satisfy a request that accepts untrusted ones.
// We need a second network factory for untrusted network requests because we need a
// different score filter for these requests.
mUntrustedNetworkFactory = mWifiInjector.makeUntrustedWifiNetworkFactory(
- mNetworkCapabilitiesFilter, mWifiConnectivityManager);
+ factoryNetworkCapabilities, mWifiConnectivityManager);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
@@ -1063,6 +1068,7 @@ public class ClientModeImpl extends StateMachine {
mWifiConfigManager.enableVerboseLogging(verbose);
mSupplicantStateTracker.enableVerboseLogging(verbose);
mPasspointManager.enableVerboseLogging(verbose);
+ mNetworkFactory.enableVerboseLogging(verbose);
}
private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL";
@@ -5763,4 +5769,20 @@ public class ClientModeImpl extends StateMachine {
resultMsg.recycle();
return result;
}
+
+ /**
+ * Add a network request match callback to {@link WifiNetworkFactory}.
+ */
+ public void addNetworkRequestMatchCallback(IBinder binder,
+ INetworkRequestMatchCallback callback,
+ int callbackIdentifier) {
+ mNetworkFactory.addCallback(binder, callback, callbackIdentifier);
+ }
+
+ /**
+ * Remove a network request match callback from {@link WifiNetworkFactory}.
+ */
+ public void removeNetworkRequestMatchCallback(int callbackIdentifier) {
+ mNetworkFactory.removeCallback(callbackIdentifier);
+ }
}
diff --git a/service/java/com/android/server/wifi/ScanResultMatchInfo.java b/service/java/com/android/server/wifi/ScanResultMatchInfo.java
index ad29c2312..72fab6bc0 100644
--- a/service/java/com/android/server/wifi/ScanResultMatchInfo.java
+++ b/service/java/com/android/server/wifi/ScanResultMatchInfo.java
@@ -15,11 +15,15 @@
*/
package com.android.server.wifi;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import com.android.server.wifi.util.ScanResultUtil;
+import java.lang.annotation.Retention;
import java.util.Objects;
/**
@@ -31,6 +35,15 @@ public class ScanResultMatchInfo {
public static final int NETWORK_TYPE_PSK = 2;
public static final int NETWORK_TYPE_EAP = 3;
+ @Retention(SOURCE)
+ @IntDef(prefix = { "NETWORK_TYPE_" }, value = {
+ NETWORK_TYPE_OPEN,
+ NETWORK_TYPE_WEP,
+ NETWORK_TYPE_PSK,
+ NETWORK_TYPE_EAP
+ })
+ public @interface NetworkType {}
+
/**
* SSID of the network.
*/
@@ -38,29 +51,51 @@ public class ScanResultMatchInfo {
/**
* Security Type of the network.
*/
- public int networkType;
+ public @NetworkType int networkType;
/**
- * Get the ScanResultMatchInfo for the given WifiConfiguration
+ * Fetch network type from network configuration.
*/
- public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
- ScanResultMatchInfo info = new ScanResultMatchInfo();
- info.networkSsid = config.SSID;
+ public static @NetworkType int getNetworkType(WifiConfiguration config) {
if (WifiConfigurationUtil.isConfigForPskNetwork(config)) {
- info.networkType = NETWORK_TYPE_PSK;
+ return NETWORK_TYPE_PSK;
} else if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
- info.networkType = NETWORK_TYPE_EAP;
+ return NETWORK_TYPE_EAP;
} else if (WifiConfigurationUtil.isConfigForWepNetwork(config)) {
- info.networkType = NETWORK_TYPE_WEP;
+ return NETWORK_TYPE_WEP;
} else if (WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
- info.networkType = NETWORK_TYPE_OPEN;
- } else {
- throw new IllegalArgumentException("Invalid WifiConfiguration: " + config);
+ return NETWORK_TYPE_OPEN;
}
+ throw new IllegalArgumentException("Invalid WifiConfiguration: " + config);
+ }
+
+ /**
+ * Get the ScanResultMatchInfo for the given WifiConfiguration
+ */
+ public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
+ ScanResultMatchInfo info = new ScanResultMatchInfo();
+ info.networkSsid = config.SSID;
+ info.networkType = getNetworkType(config);
return info;
}
/**
+ * Fetch network type from scan result.
+ */
+ public static @NetworkType int getNetworkType(ScanResult scanResult) {
+ if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
+ return NETWORK_TYPE_PSK;
+ } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
+ return NETWORK_TYPE_EAP;
+ } else if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
+ return NETWORK_TYPE_WEP;
+ } else if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
+ return NETWORK_TYPE_OPEN;
+ }
+ throw new IllegalArgumentException("Invalid ScanResult: " + scanResult);
+ }
+
+ /**
* Get the ScanResultMatchInfo for the given ScanResult
*/
public static ScanResultMatchInfo fromScanResult(ScanResult scanResult) {
@@ -70,17 +105,7 @@ public class ScanResultMatchInfo {
// However, according to our public documentation ths {@link WifiConfiguration#SSID} can
// either have a hex string or quoted ASCII string SSID.
info.networkSsid = ScanResultUtil.createQuotedSSID(scanResult.SSID);
- if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
- info.networkType = NETWORK_TYPE_PSK;
- } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
- info.networkType = NETWORK_TYPE_EAP;
- } else if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
- info.networkType = NETWORK_TYPE_WEP;
- } else if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
- info.networkType = NETWORK_TYPE_OPEN;
- } else {
- throw new IllegalArgumentException("Invalid ScanResult: " + scanResult);
- }
+ info.networkType = getNetworkType(scanResult);
return info;
}
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index 0258834ea..9bf6e327d 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -16,15 +16,21 @@
package com.android.server.wifi;
+import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
+
import android.content.pm.UserInfo;
import android.net.IpConfiguration;
+import android.net.MacAddress;
import android.net.StaticIpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiNetworkSpecifier;
import android.net.wifi.WifiScanner;
+import android.os.PatternMatcher;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.util.NativeUtil;
@@ -50,16 +56,21 @@ public class WifiConfigurationUtil {
/**
* Constants used for validating external config objects.
*/
- private static final int ENCLOSING_QUTOES_LEN = 2;
- private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUTOES_LEN;
- private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUTOES_LEN;
+ private static final int ENCLOSING_QUOTES_LEN = 2;
+ private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN;
+ private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUOTES_LEN;
private static final int SSID_HEX_MIN_LEN = 2;
private static final int SSID_HEX_MAX_LEN = 64;
- private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUTOES_LEN;
- private static final int PSK_ASCII_MAX_LEN = 63 + ENCLOSING_QUTOES_LEN;
+ private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN;
+ private static final int PSK_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN;
private static final int PSK_HEX_LEN = 64;
@VisibleForTesting
public static final String PASSWORD_MASK = "*";
+ private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
+ private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN =
+ new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
+ new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
/**
* Check whether a network configuration is visible to a user or any of its managed profiles.
@@ -319,6 +330,34 @@ public class WifiConfigurationUtil {
return true;
}
+ private static boolean validateBssid(MacAddress bssid) {
+ if (bssid == null) return true;
+ if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) {
+ Log.e(TAG, "validateBssid failed: invalid bssid");
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean validateBssid(String bssid) {
+ if (bssid == null) return true;
+ if (bssid.isEmpty()) {
+ Log.e(TAG, "validateBssid failed: empty string");
+ return false;
+ }
+ MacAddress bssidMacAddress;
+ try {
+ bssidMacAddress = MacAddress.fromString(bssid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "validateBssid failed: malformed string: " + bssid);
+ return false;
+ }
+ if (!validateBssid(bssidMacAddress)) {
+ return false;
+ }
+ return true;
+ }
+
private static boolean validatePsk(String psk, boolean isAdd) {
if (isAdd) {
if (psk == null) {
@@ -461,13 +500,14 @@ public class WifiConfigurationUtil {
*
* This method checks for the following parameters:
* 1. {@link WifiConfiguration#SSID}
- * 2. {@link WifiConfiguration#preSharedKey}
- * 3. {@link WifiConfiguration#allowedKeyManagement}
- * 4. {@link WifiConfiguration#allowedProtocols}
- * 5. {@link WifiConfiguration#allowedAuthAlgorithms}
- * 6. {@link WifiConfiguration#allowedGroupCiphers}
- * 7. {@link WifiConfiguration#allowedPairwiseCiphers}
- * 8. {@link WifiConfiguration#getIpConfiguration()}
+ * 2. {@link WifiConfiguration#BSSID}
+ * 3. {@link WifiConfiguration#preSharedKey}
+ * 4. {@link WifiConfiguration#allowedKeyManagement}
+ * 5. {@link WifiConfiguration#allowedProtocols}
+ * 6. {@link WifiConfiguration#allowedAuthAlgorithms}
+ * 7. {@link WifiConfiguration#allowedGroupCiphers}
+ * 8. {@link WifiConfiguration#allowedPairwiseCiphers}
+ * 9. {@link WifiConfiguration#getIpConfiguration()}
*
* @param config {@link WifiConfiguration} received from an external application.
* @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add,
@@ -480,6 +520,9 @@ public class WifiConfigurationUtil {
if (!validateSsid(config.SSID, isAdd)) {
return false;
}
+ if (!validateBssid(config.BSSID)) {
+ return false;
+ }
if (!validateBitSets(config)) {
return false;
}
@@ -497,6 +540,134 @@ public class WifiConfigurationUtil {
return true;
}
+ private static boolean validateBssidPattern(
+ Pair<MacAddress, MacAddress> bssidPatternMatcher) {
+ if (bssidPatternMatcher == null) return true;
+ MacAddress baseAddress = bssidPatternMatcher.first;
+ MacAddress mask = bssidPatternMatcher.second;
+ if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) {
+ Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress);
+ return false;
+ }
+ if (mask.equals(MacAddress.ALL_ZEROS_ADDRESS)
+ && !baseAddress.equals(MacAddress.ALL_ZEROS_ADDRESS)) {
+ Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/"
+ + baseAddress);
+ return false;
+ }
+ // TBD: Can we do any more sanity checks?
+ return true;
+ }
+
+ // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}.
+ // Merge them somehow?.
+ private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) {
+ PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
+ Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
+ if (ssidPatternMatcher == null || bssidPatternMatcher == null) {
+ return false;
+ }
+ if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null
+ || bssidPatternMatcher.second == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) {
+ PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
+ Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
+ if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX
+ && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
+ return true;
+ }
+ if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) {
+ PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
+ Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
+ if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH)
+ && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Validate the configuration received from an external application inside
+ * {@link WifiNetworkSpecifier}.
+ *
+ * This method checks for the following parameters:
+ * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher}
+ * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher}
+ * 3. {@link WifiConfiguration#SSID}
+ * 4. {@link WifiConfiguration#BSSID}
+ * 5. {@link WifiConfiguration#preSharedKey}
+ * 6. {@link WifiConfiguration#allowedKeyManagement}
+ * 7. {@link WifiConfiguration#allowedProtocols}
+ * 8. {@link WifiConfiguration#allowedAuthAlgorithms}
+ * 9. {@link WifiConfiguration#allowedGroupCiphers}
+ * 10. {@link WifiConfiguration#allowedPairwiseCiphers}
+ * 11. {@link WifiConfiguration#getIpConfiguration()}
+ *
+ * @param specifier Instance of {@link WifiNetworkSpecifier}.
+ * @return true if the parameters are valid, false otherwise.
+ */
+ public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) {
+ if (!isValidNetworkSpecifier(specifier)) {
+ Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier");
+ return false;
+ }
+ if (isMatchNoneNetworkSpecifier(specifier)) {
+ Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier");
+ return false;
+ }
+ if (isMatchAllNetworkSpecifier(specifier)) {
+ Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier");
+ return false;
+ }
+ WifiConfiguration config = specifier.wifiConfiguration;
+ if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
+ // For literal SSID matches, the value should satisfy SSID requirements.
+ // WifiConfiguration.SSID needs quotes around ASCII SSID.
+ if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) {
+ return false;
+ }
+ } else {
+ if (config.hiddenSSID) {
+ Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported "
+ + "for hidden networks");
+ return false;
+ }
+ }
+ if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) {
+ // For literal BSSID matches, the value should satisfy MAC address requirements.
+ if (!validateBssid(specifier.bssidPatternMatcher.first)) {
+ return false;
+ }
+ } else {
+ if (!validateBssidPattern(specifier.bssidPatternMatcher)) {
+ return false;
+ }
+ }
+ if (!validateBitSets(config)) {
+ return false;
+ }
+ if (!validateKeyMgmt(config.allowedKeyManagement)) {
+ return false;
+ }
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
+ && !validatePsk(config.preSharedKey, true)) {
+ return false;
+ }
+ // TBD: Validate some enterprise params as well in the future here.
+ return true;
+ }
+
/**
* Check if the provided two networks are the same.
* Note: This does not check if network selection BSSID's are the same.
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 53b669e4c..e708e2bde 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -18,6 +18,7 @@ package com.android.server.wifi;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.SystemSensorManager;
@@ -548,7 +549,10 @@ public class WifiInjector {
public WifiNetworkFactory makeWifiNetworkFactory(
NetworkCapabilities nc, WifiConnectivityManager wifiConnectivityManager) {
return new WifiNetworkFactory(
- mWifiCoreHandlerThread.getLooper(), mContext, nc, wifiConnectivityManager);
+ mWifiCoreHandlerThread.getLooper(), mContext, nc,
+ (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE),
+ (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE),
+ mClock, this, wifiConnectivityManager, mWifiPermissionsUtil);
}
/**
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index 489601d0d..9da64d231 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -16,63 +16,524 @@
package com.android.server.wifi;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.content.Context;
+import android.net.MacAddress;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.INetworkRequestUserSelectionCallback;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiNetworkSpecifier;
+import android.net.wifi.WifiScanner;
+import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.ExternalCallbackTracker;
+import com.android.server.wifi.util.WifiPermissionsUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Network factory to handle trusted wifi network requests.
*/
public class WifiNetworkFactory extends NetworkFactory {
private static final String TAG = "WifiNetworkFactory";
+ @VisibleForTesting
private static final int SCORE_FILTER = 60;
+ @VisibleForTesting
+ public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds
+ private final Context mContext;
+ private final ActivityManager mActivityManager;
+ private final AlarmManager mAlarmManager;
+ private final Clock mClock;
+ private final Handler mHandler;
+ private final WifiInjector mWifiInjector;
private final WifiConnectivityManager mWifiConnectivityManager;
- private int mConnectionReqCount = 0;
+ private final WifiPermissionsUtil mWifiPermissionsUtil;
+ private final WifiScanner.ScanSettings mScanSettings;
+ private final NetworkFactoryScanListener mScanListener;
+ private final NetworkFactoryAlarmListener mPeriodicScanTimerListener;
+ private final ExternalCallbackTracker<INetworkRequestMatchCallback> mRegisteredCallbacks;
+ private WifiScanner mWifiScanner;
+
+ private int mGenericConnectionReqCount = 0;
+ private NetworkRequest mActiveSpecificNetworkRequest;
+ private WifiNetworkSpecifier mActiveSpecificNetworkRequestSpecifier;
+ private List<WifiConfiguration> mActiveMatchedNetworks;
+ // Verbose logging flag.
+ private boolean mVerboseLoggingEnabled = false;
+ private boolean mPeriodicScanTimerSet = false;
+
+ // Scan listener for scan requests.
+ private class NetworkFactoryScanListener implements WifiScanner.ScanListener {
+ @Override
+ public void onSuccess() {
+ // Scan request succeeded, wait for results to report to external clients.
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Scan request succeeded");
+ }
+ }
+
+ @Override
+ public void onFailure(int reason, String description) {
+ Log.e(TAG, "Scan failure received. reason: " + reason
+ + ", description: " + description);
+ // TODO(b/113878056): Retry scan to workaround any transient scan failures.
+ scheduleNextPeriodicScan();
+ }
+
+ @Override
+ public void onResults(WifiScanner.ScanData[] scanDatas) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Scan results received");
+ }
+ // For single scans, the array size should always be 1.
+ if (scanDatas.length != 1) {
+ Log.wtf(TAG, "Found more than 1 batch of scan results, Ignoring...");
+ return;
+ }
+ WifiScanner.ScanData scanData = scanDatas[0];
+ ScanResult[] scanResults = scanData.getResults();
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Received " + scanResults.length + " scan results");
+ }
+ List<WifiConfiguration> matchedNetworks =
+ getNetworksMatchingActiveNetworkRequest(scanResults);
+ sendNetworkRequestMatchCallbacksForActiveRequest(matchedNetworks);
+ mActiveMatchedNetworks = matchedNetworks;
+
+ // Didn't find a match, schedule the next scan.
+ scheduleNextPeriodicScan();
+ }
+
+ @Override
+ public void onFullResult(ScanResult fullScanResult) {
+ // Ignore for single scans.
+ }
- public WifiNetworkFactory(Looper l, Context c, NetworkCapabilities f,
- WifiConnectivityManager connectivityManager) {
- super(l, c, TAG, f);
+ @Override
+ public void onPeriodChanged(int periodInMs) {
+ // Ignore for single scans.
+ }
+ };
+
+ private class NetworkFactoryAlarmListener implements AlarmManager.OnAlarmListener {
+ @Override
+ public void onAlarm() {
+ // Trigger the next scan.
+ startScan();
+ }
+ }
+
+ // Callback result from settings UI.
+ private class NetworkFactoryUserSelectionCallback extends
+ INetworkRequestUserSelectionCallback.Stub {
+ @Override
+ public void select(WifiConfiguration wifiConfiguration) {
+ mHandler.post(() -> {
+ if (mActiveSpecificNetworkRequest == null) {
+ Log.e(TAG, "Stale callback select received");
+ return;
+ }
+ // TODO(b/113878056): Trigger network connection to |wificonfiguration|.
+ });
+ }
+
+ @Override
+ public void reject() {
+ mHandler.post(() -> {
+ if (mActiveSpecificNetworkRequest == null) {
+ Log.e(TAG, "Stale callback reject received");
+ return;
+ }
+ // TODO(b/113878056): Clear the active network request.
+ });
+ }
+ }
+
+ public WifiNetworkFactory(Looper looper, Context context, NetworkCapabilities nc,
+ ActivityManager activityManager, AlarmManager alarmManager,
+ Clock clock, WifiInjector wifiInjector,
+ WifiConnectivityManager connectivityManager,
+ WifiPermissionsUtil wifiPermissionsUtil) {
+ super(looper, context, TAG, nc);
+ mContext = context;
+ mActivityManager = activityManager;
+ mAlarmManager = alarmManager;
+ mClock = clock;
+ mHandler = new Handler(looper);
+ mWifiInjector = wifiInjector;
mWifiConnectivityManager = connectivityManager;
+ mWifiPermissionsUtil = wifiPermissionsUtil;
+ // Create the scan settings.
+ mScanSettings = new WifiScanner.ScanSettings();
+ mScanSettings.type = WifiScanner.TYPE_HIGH_ACCURACY;
+ mScanSettings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
+ mScanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
+ mScanListener = new NetworkFactoryScanListener();
+ mPeriodicScanTimerListener = new NetworkFactoryAlarmListener();
+ mRegisteredCallbacks = new ExternalCallbackTracker<INetworkRequestMatchCallback>(mHandler);
setScoreFilter(SCORE_FILTER);
}
+ /**
+ * Enable verbose logging.
+ */
+ public void enableVerboseLogging(int verbose) {
+ mVerboseLoggingEnabled = (verbose > 0);
+ }
+
+ /**
+ * Add a new callback for network request match handling.
+ */
+ public void addCallback(IBinder binder, INetworkRequestMatchCallback callback,
+ int callbackIdentifier) {
+ if (!mRegisteredCallbacks.add(binder, callback, callbackIdentifier)) {
+ Log.e(TAG, "Failed to add callback");
+ return;
+ }
+ // Register our user selection callback immediately.
+ try {
+ callback.onUserSelectionCallbackRegistration(new NetworkFactoryUserSelectionCallback());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to invoke user selection registration callback " + callback, e);
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Adding callback. Num callbacks: " + mRegisteredCallbacks.getNumCallbacks());
+ }
+ }
+
+ /**
+ * Remove an existing callback for network request match handling.
+ */
+ public void removeCallback(int callbackIdentifier) {
+ mRegisteredCallbacks.remove(callbackIdentifier);
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removing callback. Num callbacks: "
+ + mRegisteredCallbacks.getNumCallbacks());
+ }
+ }
+
+ /**
+ * Check whether to accept the new network connection request.
+ *
+ * All the validation of the incoming request is done in this method.
+ */
+ @Override
+ public boolean acceptRequest(NetworkRequest networkRequest, int score) {
+ NetworkSpecifier ns = networkRequest.networkCapabilities.getNetworkSpecifier();
+ // Generic wifi request. Always accept.
+ if (ns == null) {
+ // Generic wifi request. Always accept.
+ } else {
+ // Invalid network specifier.
+ if (!(ns instanceof WifiNetworkSpecifier)) {
+ Log.e(TAG, "Invalid network specifier mentioned. Rejecting");
+ return false;
+ }
+
+ WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns;
+ if (!WifiConfigurationUtil.validateNetworkSpecifier(wns)) {
+ Log.e(TAG, "Invalid network specifier."
+ + " Rejecting request from " + wns.requestorUid);
+ return false;
+ }
+ // Only allow specific wifi network request from foreground app/service.
+ if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(wns.requestorUid)
+ && !isRequestFromForegroundAppOrService(wns.requestorUid)) {
+ Log.e(TAG, "Request not from foreground app or service."
+ + " Rejecting request from " + wns.requestorUid);
+ return false;
+ }
+ // If there is a pending request, only proceed if the new request is from a foreground
+ // app.
+ if (mActiveSpecificNetworkRequest != null
+ && !mWifiPermissionsUtil.checkNetworkSettingsPermission(wns.requestorUid)
+ && !isRequestFromForegroundApp(wns.requestorUid)) {
+ WifiNetworkSpecifier aWns =
+ (WifiNetworkSpecifier) mActiveSpecificNetworkRequest
+ .networkCapabilities
+ .getNetworkSpecifier();
+ if (isRequestFromForegroundApp(aWns.requestorUid)) {
+ Log.e(TAG, "Already processing active request from a foreground app "
+ + aWns.requestorUid + ". Rejecting request from " + wns.requestorUid);
+ return false;
+ }
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Accepted network request with specifier from fg "
+ + (isRequestFromForegroundApp(wns.requestorUid) ? "app" : "service"));
+ }
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Accepted network request " + networkRequest);
+ }
+ return true;
+ }
+
+ /**
+ * Handle new network connection requests.
+ *
+ * The assumption here is that {@link #acceptRequest(NetworkRequest, int)} has already sanitized
+ * the incoming request.
+ */
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
- if (++mConnectionReqCount == 1) {
- mWifiConnectivityManager.setTrustedConnectionAllowed(true);
+ NetworkSpecifier ns = networkRequest.networkCapabilities.getNetworkSpecifier();
+ if (ns == null) {
+ // Generic wifi request. Turn on auto-join if necessary.
+ if (++mGenericConnectionReqCount == 1) {
+ mWifiConnectivityManager.setTrustedConnectionAllowed(true);
+ }
+ } else {
+ // Invalid network specifier.
+ if (!(ns instanceof WifiNetworkSpecifier)) {
+ Log.e(TAG, "Invalid network specifier mentioned. Rejecting");
+ return;
+ }
+ retrieveWifiScanner();
+
+ // Store the active network request.
+ mActiveSpecificNetworkRequest = new NetworkRequest(networkRequest);
+ WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns;
+ mActiveSpecificNetworkRequestSpecifier = new WifiNetworkSpecifier(
+ wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.wifiConfiguration,
+ wns.requestorUid);
+
+ // Trigger periodic scans for finding a network in the request.
+ startPeriodicScans();
+ // Disable Auto-join so that NetworkFactory can take control of the network selection.
+ // TODO(b/117979585): Defer turning off auto-join.
+ mWifiConnectivityManager.enable(false);
}
}
@Override
protected void releaseNetworkFor(NetworkRequest networkRequest) {
- if (mConnectionReqCount == 0) {
- Log.e(TAG, "No valid network request to release");
- return;
- }
- if (--mConnectionReqCount == 0) {
- mWifiConnectivityManager.setTrustedConnectionAllowed(false);
+ NetworkSpecifier ns = networkRequest.networkCapabilities.getNetworkSpecifier();
+ if (ns == null) {
+ // Generic wifi request. Turn off auto-join if necessary.
+ if (mGenericConnectionReqCount == 0) {
+ Log.e(TAG, "No valid network request to release");
+ return;
+ }
+ if (--mGenericConnectionReqCount == 0) {
+ mWifiConnectivityManager.setTrustedConnectionAllowed(false);
+ }
+ } else {
+ // Invalid network specifier.
+ if (!(ns instanceof WifiNetworkSpecifier)) {
+ Log.e(TAG, "Invalid network specifier mentioned. Ingoring");
+ return;
+ }
+ if (!mActiveSpecificNetworkRequest.equals(networkRequest)) {
+ Log.e(TAG, "Network specifier does not match the active request. Ignoring");
+ return;
+ }
+ // Release the active network request.
+ mActiveSpecificNetworkRequest = null;
+ mActiveSpecificNetworkRequestSpecifier = null;
+ // Cancel the periodic scans.
+ cancelPeriodicScans();
+ // Re-enable Auto-join (if there is a generic request pending).
+ if (mGenericConnectionReqCount > 0) {
+ mWifiConnectivityManager.enable(true);
+ }
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
- pw.println(TAG + ": mConnectionReqCount " + mConnectionReqCount);
+ pw.println(TAG + ": mGenericConnectionReqCount " + mGenericConnectionReqCount);
+ pw.println(TAG + ": mActiveSpecificNetworkRequest " + mActiveSpecificNetworkRequest);
}
/**
* Check if there is at-least one connection request.
*/
public boolean hasConnectionRequests() {
- return mConnectionReqCount > 0;
+ return mGenericConnectionReqCount > 0 || mActiveSpecificNetworkRequest != null;
+ }
+
+ /**
+ * Check if the request comes from foreground app/service.
+ */
+ private boolean isRequestFromForegroundAppOrService(int requestorUid) {
+ try {
+ String requestorPackageName = mContext.getPackageManager().getNameForUid(requestorUid);
+ return mActivityManager.getPackageImportance(requestorPackageName)
+ <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to check the app state", e);
+ return false;
+ }
+ }
+
+ /**
+ * Check if the request comes from foreground app.
+ */
+ private boolean isRequestFromForegroundApp(int requestorUid) {
+ try {
+ String requestorPackageName = mContext.getPackageManager().getNameForUid(requestorUid);
+ return mActivityManager.getPackageImportance(requestorPackageName)
+ <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to check the app state", e);
+ return false;
+ }
+ }
+
+ /**
+ * Helper method to populate WifiScanner handle. This is done lazily because
+ * WifiScanningService is started after WifiService.
+ */
+ private void retrieveWifiScanner() {
+ if (mWifiScanner != null) return;
+ mWifiScanner = mWifiInjector.getWifiScanner();
+ checkNotNull(mWifiScanner);
+ }
+
+ private void startPeriodicScans() {
+ if (mActiveSpecificNetworkRequestSpecifier == null) {
+ Log.e(TAG, "Periodic scan triggered when there is no active network request. "
+ + "Ignoring...");
+ return;
+ }
+ WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier;
+ WifiConfiguration wifiConfiguration = wns.wifiConfiguration;
+ if (wifiConfiguration.hiddenSSID) {
+ mScanSettings.hiddenNetworks = new WifiScanner.ScanSettings.HiddenNetwork[1];
+ // Can't search for SSID pattern in hidden networks.
+ mScanSettings.hiddenNetworks[0] =
+ new WifiScanner.ScanSettings.HiddenNetwork(
+ addEnclosingQuotes(wns.ssidPatternMatcher.getPath()));
+ }
+ startScan();
+ }
+
+ private void cancelPeriodicScans() {
+ if (mPeriodicScanTimerSet) {
+ mAlarmManager.cancel(mPeriodicScanTimerListener);
+ mPeriodicScanTimerSet = false;
+ }
+ // Clear the hidden networks field after each request.
+ mScanSettings.hiddenNetworks = null;
+ }
+
+ private void scheduleNextPeriodicScan() {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mClock.getElapsedSinceBootMillis() + PERIODIC_SCAN_INTERVAL_MS,
+ TAG, mPeriodicScanTimerListener, mHandler);
+ mPeriodicScanTimerSet = true;
+ }
+
+ private void startScan() {
+ if (mActiveSpecificNetworkRequestSpecifier == null) {
+ Log.e(TAG, "Scan triggered when there is no active network request. Ignoring...");
+ return;
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Starting the next scan for " + mActiveSpecificNetworkRequestSpecifier);
+ }
+ // Create a worksource using the caller's UID.
+ WorkSource workSource = new WorkSource(mActiveSpecificNetworkRequestSpecifier.requestorUid);
+ mWifiScanner.startScan(mScanSettings, mScanListener, workSource);
+ }
+
+ private boolean doesScanResultMatchWifiNetworkSpecifier(
+ WifiNetworkSpecifier wns, ScanResult scanResult) {
+ if (!wns.ssidPatternMatcher.match(scanResult.SSID)) {
+ return false;
+ }
+ MacAddress bssid = MacAddress.fromString(scanResult.BSSID);
+ MacAddress matchBaseAddress = wns.bssidPatternMatcher.first;
+ MacAddress matchMask = wns.bssidPatternMatcher.second;
+ if (!bssid.matches(matchBaseAddress, matchMask)) {
+ return false;
+ }
+ if (ScanResultMatchInfo.getNetworkType(wns.wifiConfiguration)
+ != ScanResultMatchInfo.getNetworkType(scanResult)) {
+ return false;
+ }
+ return true;
+ }
+
+ // Loops through the scan results and finds scan results matching the active network
+ // request. Returns a list of WifiConfiguration representing all the networks that
+ // match the active network request's specifier.
+ private List<WifiConfiguration> getNetworksMatchingActiveNetworkRequest(
+ ScanResult[] scanResults) {
+ // There could be multiple bssid's (i.e ScanResult) within the same ssid matching the
+ // specifier, we need to collapse all of them into a single network represented by a
+ // WifiConfiguration object.
+ // Use a map keyed in by pair of SSID & network type to collect the list of unique
+ // networks (i.e wificonfiguration objects) matching the specifier.
+ if (mActiveSpecificNetworkRequest == null) {
+ Log.e(TAG, "Scan results received with no active network request. Ignoring...");
+ return new ArrayList<>();
+ }
+ Map<Pair<String, Integer>, WifiConfiguration> matchedNetworks = new HashMap<>();
+ WifiNetworkSpecifier wns = (WifiNetworkSpecifier)
+ mActiveSpecificNetworkRequest.networkCapabilities.getNetworkSpecifier();
+ checkNotNull(wns);
+
+ for (ScanResult scanResult : scanResults) {
+ if (doesScanResultMatchWifiNetworkSpecifier(wns, scanResult)) {
+ // Use the WifiConfiguration provided in the request & fill in the SSID
+ // & BSSID fields from ScanResult.
+ WifiConfiguration matchedNetwork = new WifiConfiguration(wns.wifiConfiguration);
+ matchedNetwork.SSID = addEnclosingQuotes(scanResult.SSID);
+ matchedNetwork.BSSID = scanResult.BSSID;
+ matchedNetworks.put(
+ Pair.create(matchedNetwork.SSID,
+ ScanResultMatchInfo.getNetworkType(matchedNetwork)),
+ matchedNetwork);
+ }
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "List of networks matching the active request "
+ + matchedNetworks.values());
+ }
+ return new ArrayList<>(matchedNetworks.values());
+ }
+
+ private void sendNetworkRequestMatchCallbacksForActiveRequest(
+ List<WifiConfiguration> matchedNetworks) {
+ if (mRegisteredCallbacks.getNumCallbacks() == 0) {
+ Log.e(TAG, "No callback registered for sending network request matches. "
+ + "Ignoring...");
+ return;
+ }
+ for (INetworkRequestMatchCallback callback : mRegisteredCallbacks.getCallbacks()) {
+ try {
+ callback.onMatch(matchedNetworks);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to invoke network request match callback " + callback, e);
+ }
+ }
}
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 6206d9b15..4b1766d79 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -58,6 +58,7 @@ import android.net.Network;
import android.net.NetworkUtils;
import android.net.Uri;
import android.net.ip.IpClient;
+import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.IWifiManager;
@@ -75,6 +76,7 @@ import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -2822,7 +2824,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
/**
* see {@link android.net.wifi.WifiManager#registerTrafficStateCallback(
- * TrafficStateCallback, Handler)}
+ * WifiManager.TrafficStateCallback, Handler)}
*
* @param binder IBinder instance to allow cleanup if the app dies
* @param callback Traffic State callback to register
@@ -2890,4 +2892,61 @@ public class WifiServiceImpl extends IWifiManager.Stub {
private static boolean hasAutomotiveFeature(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
+
+ /**
+ * see {@link android.net.wifi.WifiManager#registerNetworkRequestMatchCallback(
+ * WifiManager.NetworkRequestMatchCallback, Handler)} (
+ *
+ * @param binder IBinder instance to allow cleanup if the app dies
+ * @param callback Network Request Match callback to register
+ * @param callbackIdentifier Unique ID of the registering callback. This ID will be used to
+ * unregister the callback.
+ * See {@link #unregisterNetworkRequestMatchCallback(int)} (int)}
+ *
+ * @throws SecurityException if the caller does not have permission to register a callback
+ * @throws RemoteException if remote exception happens
+ * @throws IllegalArgumentException if the arguments are null or invalid
+ */
+ @Override
+ public void registerNetworkRequestMatchCallback(IBinder binder,
+ INetworkRequestMatchCallback callback,
+ int callbackIdentifier) {
+ // verify arguments
+ if (binder == null) {
+ throw new IllegalArgumentException("Binder must not be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ enforceNetworkSettingsPermission();
+ if (mVerboseLoggingEnabled) {
+ mLog.info("registerNetworkRequestMatchCallback uid=%")
+ .c(Binder.getCallingUid()).flush();
+ }
+ // Post operation to handler thread
+ mClientHandler.post(() -> {
+ mClientModeImpl.addNetworkRequestMatchCallback(binder, callback, callbackIdentifier);
+ });
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#unregisterNetworkRequestMatchCallback(
+ * WifiManager.NetworkRequestMatchCallback)}
+ *
+ * @param callbackIdentifier Unique ID of the callback to be unregistered.
+ *
+ * @throws SecurityException if the caller does not have permission to register a callback
+ */
+ @Override
+ public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) {
+ enforceNetworkSettingsPermission();
+ if (mVerboseLoggingEnabled) {
+ mLog.info("unregisterNetworkRequestMatchCallback uid=%")
+ .c(Binder.getCallingUid()).flush();
+ }
+ // Post operation to handler thread
+ mClientHandler.post(() -> {
+ mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier);
+ });
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index cdc9590b9..97d7e4f22 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
@@ -425,12 +425,9 @@ public class WifiConfigurationTestUtil {
}
/**
- * Creates a scan detail corresponding to the provided network and given BSSID, level &frequency
- * values.
+ * Gets scan result capabilities for a particular network configuration.
*/
- public static ScanDetail createScanDetailForNetwork(
- WifiConfiguration configuration, String bssid, int level, int frequency,
- long tsf, long seen) {
+ public static String getScanResultCapsForNetwork(WifiConfiguration configuration) {
String caps;
if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
caps = "[WPA2-PSK-CCMP]";
@@ -443,6 +440,16 @@ public class WifiConfigurationTestUtil {
} else {
caps = "[]";
}
+ return caps;
+ }
+
+ /**
+ * Creates a scan detail corresponding to the provided network and given BSSID, etc.
+ */
+ public static ScanDetail createScanDetailForNetwork(
+ WifiConfiguration configuration, String bssid, int level, int frequency,
+ long tsf, long seen) {
+ String caps = getScanResultCapsForNetwork(configuration);
WifiSsid ssid = WifiSsid.createFromAsciiEncoded(configuration.getPrintableSsid());
return new ScanDetail(ssid, bssid, caps, level, frequency, tsf, seen);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index 3774feca3..089ae425d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -20,11 +20,15 @@ import static org.junit.Assert.*;
import android.content.pm.UserInfo;
import android.net.IpConfiguration;
+import android.net.MacAddress;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiNetworkSpecifier;
import android.net.wifi.WifiScanner;
+import android.os.PatternMatcher;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
+import android.util.Pair;
import org.junit.Test;
@@ -42,6 +46,7 @@ public class WifiConfigurationUtilTest {
static final int CURRENT_USER_ID = 0;
static final int CURRENT_USER_MANAGED_PROFILE_USER_ID = 10;
static final int OTHER_USER_ID = 11;
+ static final int TEST_UID = 10000;
static final String TEST_SSID = "test_ssid";
static final String TEST_SSID_1 = "test_ssid_1";
static final String TEST_BSSID = "aa:aa:11:22:cc:dd";
@@ -490,6 +495,118 @@ public class WifiConfigurationUtilTest {
}
/**
+ * Verify that the validate method successfully validates good WifiNetworkSpecifier with
+ * only ssid pattern set.
+ */
+ @Test
+ public void testValidateNetworkSpecifierPositiveCases_SsidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL),
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method successfully validates good WifiNetworkSpecifier with
+ * only bssid pattern set.
+ */
+ @Test
+ public void testValidateNetworkSpecifierPositiveCases_BssidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB),
+ Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method successfully validates good WifiNetworkSpecifier with
+ * both ssid & bssid patterns set.
+ */
+ @Test
+ public void testValidateNetworkSpecifierPositiveCases_BothSsidPatternAndBssidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL),
+ Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiNetworkSpecifier with no
+ * ssid/bssid info.
+ */
+ @Test
+ public void testValidateNetworkSpecifierNegativeCases_NoSsidBssid() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB),
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiNetworkSpecifier with invalid SSID
+ * match pattern.
+ */
+ @Test
+ public void testValidateNetworkSpecifierNegativeCases_MatchNoneSsidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher("", PatternMatcher.PATTERN_LITERAL),
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiNetworkSpecifier with illegal
+ * pattern.
+ */
+ @Test
+ public void testValidateNetworkSpecifierNegativeCases_MatchNoneBssidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL),
+ Pair.create(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiNetworkSpecifier with illegal
+ * pattern.
+ */
+ @Test
+ public void testValidateNetworkSpecifierNegativeCases_InvalidBssidPattern() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL),
+ Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.ALL_ZEROS_ADDRESS),
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ TEST_UID);
+ assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiNetworkSpecifier with SSID pattern
+ * for hidden network.
+ */
+ @Test
+ public void testValidateNetworkSpecifierNegativeCases_NoSsidPatternForHiddenNetwork() {
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_PREFIX),
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+ WifiConfigurationTestUtil.createOpenHiddenNetwork(),
+ TEST_UID);
+ assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier));
+ }
+
+ /**
* Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
* for an open network using {@link WifiConfigurationUtil#createPnoNetwork(
* WifiConfiguration)}.
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
index 46519e519..eaaaf5704 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -16,31 +16,91 @@
package com.android.server.wifi;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+
+import static com.android.server.wifi.WifiNetworkFactory.PERIODIC_SCAN_INTERVAL_MS;
+import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
+
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.MacAddress;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.INetworkRequestUserSelectionCallback;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiNetworkSpecifier;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiScanner.ScanListener;
+import android.net.wifi.WifiScanner.ScanSettings;
+import android.os.IBinder;
+import android.os.PatternMatcher;
+import android.os.WorkSource;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.server.wifi.util.WifiPermissionsUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* Unit tests for {@link com.android.server.wifi.WifiNetworkFactory}.
*/
@SmallTest
public class WifiNetworkFactoryTest {
+ private static final int TEST_UID_1 = 10423;
+ private static final int TEST_UID_2 = 10424;
+ private static final int TEST_CALLBACK_IDENTIFIER = 123;
+ private static final String TEST_PACKAGE_NAME_1 = "com.test.networkrequest.1";
+ private static final String TEST_PACKAGE_NAME_2 = "com.test.networkrequest.2";
+ private static final String TEST_SSID_1 = "test1234";
+ private static final String TEST_SSID_2 = "test12345678";
+ private static final String TEST_SSID_3 = "abc1234";
+ private static final String TEST_SSID_4 = "abc12345678";
+ private static final String TEST_BSSID_1 = "12:34:23:23:45:ac";
+ private static final String TEST_BSSID_2 = "12:34:23:32:12:67";
+ private static final String TEST_BSSID_3 = "45:34:34:12:bb:dd";
+ private static final String TEST_BSSID_4 = "45:34:34:56:ee:ff";
+ private static final String TEST_BSSID_1_2_OUI = "12:34:23:00:00:00";
+ private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+
@Mock WifiConnectivityManager mWifiConnectivityManager;
@Mock Context mContext;
+ @Mock ActivityManager mActivityManager;
+ @Mock AlarmManager mAlarmManager;
+ @Mock Clock mClock;
+ @Mock WifiInjector mWifiInjector;
+ @Mock WifiPermissionsUtil mWifiPermissionsUtil;
+ @Mock WifiScanner mWifiScanner;
+ @Mock PackageManager mPackageManager;
+ @Mock IBinder mAppBinder;
+ @Mock INetworkRequestMatchCallback mNetworkRequestMatchCallback;
NetworkCapabilities mNetworkCapabilities;
TestLooper mLooper;
NetworkRequest mNetworkRequest;
+ WifiScanner.ScanData[] mTestScanDatas;
+ ArgumentCaptor<ScanSettings> mScanSettingsArgumentCaptor =
+ ArgumentCaptor.forClass(ScanSettings.class);
+ ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor =
+ ArgumentCaptor.forClass(WorkSource.class);
private WifiNetworkFactory mWifiNetworkFactory;
@@ -54,9 +114,20 @@ public class WifiNetworkFactoryTest {
mLooper = new TestLooper();
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ mTestScanDatas = ScanTestUtil.createScanDatas(new int[][]{ { 2417, 2427, 5180, 5170 } });
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getNameForUid(TEST_UID_1)).thenReturn(TEST_PACKAGE_NAME_1);
+ when(mPackageManager.getNameForUid(TEST_UID_2)).thenReturn(TEST_PACKAGE_NAME_2);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+ when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
mWifiNetworkFactory = new WifiNetworkFactory(mLooper.getLooper(), mContext,
- mNetworkCapabilities, mWifiConnectivityManager);
+ mNetworkCapabilities, mActivityManager, mAlarmManager, mClock, mWifiInjector,
+ mWifiConnectivityManager, mWifiPermissionsUtil);
mNetworkRequest = new NetworkRequest.Builder()
.setCapabilities(mNetworkCapabilities)
@@ -75,7 +146,7 @@ public class WifiNetworkFactoryTest {
* Validates handling of needNetworkFor.
*/
@Test
- public void testHandleNetworkRequest() {
+ public void testHandleNetworkRequestWithNoSpecifier() {
assertFalse(mWifiNetworkFactory.hasConnectionRequests());
mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
@@ -92,7 +163,7 @@ public class WifiNetworkFactoryTest {
* Validates handling of releaseNetwork.
*/
@Test
- public void testHandleNetworkRelease() {
+ public void testHandleNetworkReleaseWithNoSpecifier() {
// Release network with out a corresponding request should be ignored.
mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
assertFalse(mWifiNetworkFactory.hasConnectionRequests());
@@ -105,4 +176,706 @@ public class WifiNetworkFactoryTest {
assertFalse(mWifiNetworkFactory.hasConnectionRequests());
verify(mWifiConnectivityManager).setTrustedConnectionAllowed(false);
}
+
+ /**
+ * Validates handling of acceptNetwork for requests with no network specifier.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestWithNoSpecifier() {
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a non foreground
+ * app/service.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromNonFgAppOrSvcWithSpecifier() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE + 1);
+
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+
+ assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a foreground
+ * app.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromFgAppWithSpecifier() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND);
+
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from apps holding
+ * NETWORK_SETTINGS.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromNetworkSettingAppWithSpecifier() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_GONE);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID_1))
+ .thenReturn(true);
+
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a foreground
+ * app.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromFgAppWithSpecifierWithPendingRequestFromFgSvc() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND);
+
+ // Handle request 1.
+ WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Make request 2 which will be accepted because a fg app request can
+ // override a fg service request.
+ WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2);
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a foreground
+ * app.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromFgSvcWithSpecifierWithPendingRequestFromFgSvc() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+
+ // Handle request 1.
+ WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Make request 2 which will be accepted because a fg service request can
+ // override an existing fg service request.
+ WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2);
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a foreground
+ * app.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromFgAppWithSpecifierWithPendingRequestFromFgApp() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND);
+
+ // Handle request 1.
+ WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Make request 2 which will be accepted because a fg app request can
+ // override an existing fg app request.
+ WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2);
+ assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Validates handling of acceptNetwork with a network specifier from a foreground
+ * service when we're in the midst of processing a request from a foreground app.
+ */
+ @Test
+ public void testHandleAcceptNetworkRequestFromFgSvcWithSpecifierWithPendingRequestFromFgApp() {
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
+ .thenReturn(IMPORTANCE_FOREGROUND);
+ when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2))
+ .thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
+
+ // Handle request 1.
+ WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Make request 2 which will be rejected because a fg service request cannot
+ // override a fg app request.
+ WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2);
+ assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0));
+ }
+
+ /**
+ * Verify handling of new network request with network specifier.
+ */
+ @Test
+ public void testHandleNetworkRequestWithSpecifier() {
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Disable connectivity manager .
+ verify(mWifiConnectivityManager).enable(false);
+ verify(mWifiScanner).startScan(mScanSettingsArgumentCaptor.capture(), any(),
+ mWorkSourceArgumentCaptor.capture());
+
+ // Verify scan settings.
+ ScanSettings scanSettings = mScanSettingsArgumentCaptor.getValue();
+ assertNotNull(scanSettings);
+ assertEquals(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, scanSettings.band);
+ assertEquals(WifiScanner.TYPE_HIGH_ACCURACY, scanSettings.type);
+ assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, scanSettings.reportEvents);
+ assertNull(scanSettings.hiddenNetworks);
+ WorkSource workSource = mWorkSourceArgumentCaptor.getValue();
+ assertNotNull(workSource);
+ assertEquals(TEST_UID_1, workSource.get(0));
+ }
+
+ /**
+ * Verify handling of new network request with network specifier for a hidden network.
+ */
+ @Test
+ public void testHandleNetworkRequestWithSpecifierForHiddenNetwork() {
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, true);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Disable connectivity manager .
+ verify(mWifiConnectivityManager).enable(false);
+ verify(mWifiScanner).startScan(mScanSettingsArgumentCaptor.capture(), any(),
+ mWorkSourceArgumentCaptor.capture());
+
+ // Verify scan settings.
+ ScanSettings scanSettings = mScanSettingsArgumentCaptor.getValue();
+ assertNotNull(scanSettings);
+ assertEquals(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, scanSettings.band);
+ assertEquals(WifiScanner.TYPE_HIGH_ACCURACY, scanSettings.type);
+ assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, scanSettings.reportEvents);
+ assertNotNull(scanSettings.hiddenNetworks);
+ assertNotNull(scanSettings.hiddenNetworks[0]);
+ assertEquals(scanSettings.hiddenNetworks[0].ssid,
+ addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()));
+ WorkSource workSource = mWorkSourceArgumentCaptor.getValue();
+ assertNotNull(workSource);
+ assertEquals(TEST_UID_1, workSource.get(0));
+ }
+
+ /**
+ * Verify handling of new network request with network specifier for a non-hidden network
+ * after processing a previous hidden network requst.
+ * Validates that the scan settings was properly reset between the 2 request
+ * {@link ScanSettings#hiddenNetworks}
+ */
+ @Test
+ public void testHandleNetworkRequestWithSpecifierAfterPreviousHiddenNetworkRequest() {
+ testHandleNetworkRequestWithSpecifierForHiddenNetwork();
+ mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
+ reset(mWifiScanner, mWifiConnectivityManager);
+ testHandleNetworkRequestWithSpecifier();
+ }
+
+ /**
+ * Verify handling of release of the active network request with network specifier.
+ */
+ @Test
+ public void testHandleNetworkReleaseWithSpecifier() {
+ // Make a generic request first to ensure that we re-enable auto-join after release.
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+
+ // Make the network request with specifier.
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+ // Disable connectivity manager .
+ verify(mWifiConnectivityManager).enable(false);
+ verify(mWifiScanner).startScan(any(), any(), any());
+
+ // Release the network request.
+ mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
+ // Re-enable connectivity manager .
+ verify(mWifiConnectivityManager).enable(true);
+ }
+
+ /**
+ * Verify the periodic scan to find a network matching the network specifier.
+ * Simulates the case where the network is not found in any of the scan results.
+ */
+ @Test
+ public void testPeriodicScanNetworkRequestWithSpecifier() {
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0,
+ PERIODIC_SCAN_INTERVAL_MS, // 10s
+ PERIODIC_SCAN_INTERVAL_MS, // 10s
+ PERIODIC_SCAN_INTERVAL_MS, // 10s
+ PERIODIC_SCAN_INTERVAL_MS); // 10s
+ }
+
+ /**
+ * Verify the periodic scan back off to find a network matching the network specifier
+ * is cancelled when the active network request is released.
+ */
+ @Test
+ public void testPeriodicScanCancelOnReleaseNetworkRequestWithSpecifier() {
+ WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0,
+ PERIODIC_SCAN_INTERVAL_MS, // 10s
+ PERIODIC_SCAN_INTERVAL_MS); // 10s
+
+ mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
+ // Cancel the alarm set for the next scan.
+ verify(mAlarmManager).cancel(any(OnAlarmListener.class));
+ }
+
+ /**
+ * Verify callback registration/unregistration.
+ */
+ @Test
+ public void testHandleCallbackRegistrationAndUnregistration() throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+
+ //Ensure that we register the user selection callback using the newly registered callback.
+ verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
+ any(INetworkRequestUserSelectionCallback.class));
+
+ // TBD: Need to hook up the matching logic to invoke these callbacks to actually
+ // verify that they're in the database.
+ mWifiNetworkFactory.removeCallback(TEST_CALLBACK_IDENTIFIER);
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a specific SSID match using
+ * 4 WPA_PSK scan results, each with unique SSID.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatch() throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_WPA_PSK,
+ TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We only expect 1 network match in this case.
+ assertEquals(1, matchedNetworksCaptor.getValue().size());
+ WifiConfiguration matchedNetwork = matchedNetworksCaptor.getValue().get(0);
+ assertEquals("\"" + TEST_SSID_1 + "\"", matchedNetwork.SSID);
+ assertEquals(TEST_BSSID_1, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK));
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a Prefix SSID match using
+ * 4 open scan results, each with unique SSID.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingPrefixSsidMatch() throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_OPEN,
+ TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_PREFIX);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We expect 2 network matches in this case.
+ assertEquals(2, matchedNetworksCaptor.getValue().size());
+ for (WifiConfiguration matchedNetwork : matchedNetworksCaptor.getValue()) {
+ if (matchedNetwork.SSID.equals("\"" + TEST_SSID_1 + "\"")) {
+ assertEquals(TEST_BSSID_1, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.NONE));
+ } else if (matchedNetwork.SSID.equals("\"" + TEST_SSID_2 + "\"")) {
+ assertEquals(TEST_BSSID_2, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.NONE));
+ } else {
+ fail();
+ }
+ }
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a specific BSSID match using
+ * 4 WPA_PSK scan results, each with unique SSID.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingLiteralBssidMatch() throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_WPA_PSK,
+ TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.fromString(TEST_BSSID_1), MacAddress.BROADCAST_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We only expect 1 network match in this case.
+ assertEquals(1, matchedNetworksCaptor.getValue().size());
+ WifiConfiguration matchedNetwork = matchedNetworksCaptor.getValue().get(0);
+ assertEquals("\"" + TEST_SSID_1 + "\"", matchedNetwork.SSID);
+ assertEquals(TEST_BSSID_1, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK));
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a prefix BSSID match using
+ * 4 WPA_EAP scan results, each with unique SSID.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingOuiPrefixBssidMatch() throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_WPA_EAP,
+ TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.fromString(TEST_BSSID_1_2_OUI),
+ MacAddress.fromString(TEST_BSSID_OUI_MASK));
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We expect 2 network matches in this case.
+ assertEquals(2, matchedNetworksCaptor.getValue().size());
+ for (WifiConfiguration matchedNetwork : matchedNetworksCaptor.getValue()) {
+ if (matchedNetwork.SSID.equals("\"" + TEST_SSID_1 + "\"")) {
+ assertEquals(TEST_BSSID_1, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ } else if (matchedNetwork.SSID.equals("\"" + TEST_SSID_2 + "\"")) {
+ assertEquals(TEST_BSSID_2, matchedNetwork.BSSID);
+ assertTrue(matchedNetwork.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ } else {
+ fail();
+ }
+ }
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a specific SSID match using
+ * 4 WPA_PSK scan results, 3 of which have the same SSID.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchWithMultipleBssidMatches()
+ throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_WPA_PSK,
+ TEST_SSID_1, TEST_SSID_1, TEST_SSID_1, TEST_SSID_2);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We still only expect 1 network match in this case.
+ assertEquals(1, matchedNetworksCaptor.getValue().size());
+ WifiConfiguration matchedNetwork = matchedNetworksCaptor.getValue().get(0);
+ assertEquals("\"" + TEST_SSID_1 + "\"", matchedNetwork.SSID);
+ assertTrue(TEST_BSSID_1.equals(matchedNetwork.BSSID)
+ || TEST_BSSID_2.equals(matchedNetwork.BSSID)
+ || TEST_BSSID_3.equals(matchedNetwork.BSSID));
+ assertTrue(matchedNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK));
+ }
+
+ /**
+ * Verify network specifier match failure for a specifier containing a specific SSID match using
+ * 4 WPA_PSK scan results, 2 of which SSID_1 and the other 2 SSID_2. But, none of the scan
+ * results' SSID match the one requested in the specifier.
+ */
+ @Test
+ public void testNetworkSpecifierMatchFailUsingLiteralSsidMatchWhenSsidNotFound()
+ throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_WPA_PSK,
+ TEST_SSID_1, TEST_SSID_1, TEST_SSID_2, TEST_SSID_2);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_3, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We expect no network match in this case.
+ assertEquals(0, matchedNetworksCaptor.getValue().size());
+ }
+
+ /**
+ * Verify network specifier match failure for a specifier containing a specific SSID match using
+ * 4 open scan results, each with unique SSID. But, none of the scan
+ * results' key mgmt match the one requested in the specifier.
+ */
+ @Test
+ public void testNetworkSpecifierMatchFailUsingLiteralSsidMatchWhenKeyMgmtDiffers()
+ throws Exception {
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Setup scan data for open networks.
+ setupScanData(SCAN_RESULT_TYPE_OPEN,
+ TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4);
+
+ // Setup network specifier for open networks.
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_PREFIX);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ verify(mWifiConnectivityManager).enable(false);
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ ArgumentCaptor<List<WifiConfiguration>> matchedNetworksCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedNetworksCaptor.capture());
+
+ assertNotNull(matchedNetworksCaptor.getValue());
+ // We expect no network match in this case.
+ assertEquals(0, matchedNetworksCaptor.getValue().size());
+ }
+
+ // Simulates the periodic scans performed to find a matching network.
+ // a) Start scan
+ // b) Scan results received.
+ // c) Set alarm for next scan at the expected interval.
+ // d) Alarm fires, go to step a) again and repeat.
+ private void verifyPeriodicScans(long...expectedIntervalsInSeconds) {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+
+ ArgumentCaptor<OnAlarmListener> alarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(OnAlarmListener.class);
+ OnAlarmListener alarmListener = null;
+ ArgumentCaptor<ScanListener> scanListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ScanListener.class);
+ ScanListener scanListener = null;
+
+ InOrder inOrder = inOrder(mWifiScanner, mAlarmManager);
+
+ for (int i = 0; i < expectedIntervalsInSeconds.length - 1; i++) {
+ long expectedCurrentIntervalInMs = expectedIntervalsInSeconds[i];
+ long expectedNextIntervalInMs = expectedIntervalsInSeconds[i + 1];
+
+ // First scan is immediately fired, so need for the alarm to fire.
+ if (expectedCurrentIntervalInMs != 0) {
+ // Fire the alarm and ensure that we started the next scan.
+ alarmListener.onAlarm();
+ }
+ inOrder.verify(mWifiScanner).startScan(
+ any(), scanListenerArgumentCaptor.capture(), any());
+ scanListener = scanListenerArgumentCaptor.getValue();
+ assertNotNull(scanListener);
+
+ // Now trigger the scan results callback and verify the alarm set for the next scan.
+ scanListener.onResults(mTestScanDatas);
+
+ inOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(expectedNextIntervalInMs), any(),
+ alarmListenerArgumentCaptor.capture(), any());
+ alarmListener = alarmListenerArgumentCaptor.getValue();
+ assertNotNull(alarmListener);
+ }
+
+ verifyNoMoreInteractions(mWifiScanner, mAlarmManager);
+ }
+
+ private WifiNetworkSpecifier createWifiNetworkSpecifier(int uid, boolean isHidden) {
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ WifiConfiguration wifiConfiguration;
+ if (isHidden) {
+ wifiConfiguration = WifiConfigurationTestUtil.createPskHiddenNetwork();
+ } else {
+ wifiConfiguration = WifiConfigurationTestUtil.createPskNetwork();
+ }
+ return new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, uid);
+ }
+
+ private static final int SCAN_RESULT_TYPE_OPEN = 0;
+ private static final int SCAN_RESULT_TYPE_WPA_PSK = 1;
+ private static final int SCAN_RESULT_TYPE_WPA_EAP = 2;
+
+ private String getScanResultCapsForType(int scanResultType) {
+ switch (scanResultType) {
+ case SCAN_RESULT_TYPE_OPEN:
+ return WifiConfigurationTestUtil.getScanResultCapsForNetwork(
+ WifiConfigurationTestUtil.createOpenNetwork());
+ case SCAN_RESULT_TYPE_WPA_PSK:
+ return WifiConfigurationTestUtil.getScanResultCapsForNetwork(
+ WifiConfigurationTestUtil.createPskNetwork());
+ case SCAN_RESULT_TYPE_WPA_EAP:
+ return WifiConfigurationTestUtil.getScanResultCapsForNetwork(
+ WifiConfigurationTestUtil.createEapNetwork());
+ }
+ fail("Invalid scan result type " + scanResultType);
+ return "";
+ }
+
+ // Helper method to setup the scan data for verifying the matching algo.
+ private void setupScanData(int scanResultType, String ssid1, String ssid2, String ssid3,
+ String ssid4) {
+ // 4 scan results,
+ assertEquals(1, mTestScanDatas.length);
+ ScanResult[] scanResults = mTestScanDatas[0].getResults();
+ assertEquals(4, scanResults.length);
+
+ String caps = getScanResultCapsForType(scanResultType);
+
+ scanResults[0].SSID = ssid1;
+ scanResults[0].BSSID = TEST_BSSID_1;
+ scanResults[0].capabilities = caps;
+ scanResults[1].SSID = ssid2;
+ scanResults[1].BSSID = TEST_BSSID_2;
+ scanResults[1].capabilities = caps;
+ scanResults[2].SSID = ssid3;
+ scanResults[2].BSSID = TEST_BSSID_3;
+ scanResults[2].capabilities = caps;
+ scanResults[3].SSID = ssid4;
+ scanResults[3].BSSID = TEST_BSSID_4;
+ scanResults[3].capabilities = caps;
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index b4c7014ba..171b1a907 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -66,6 +66,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
+import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ScanResult;
@@ -139,6 +140,7 @@ public class WifiServiceImplTest {
private static final int OTHER_TEST_UID = 1300000;
private static final int TEST_USER_HANDLE = 13;
private static final int TEST_TRAFFIC_STATE_CALLBACK_IDENTIFIER = 17;
+ private static final int TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER = 234;
private static final String WIFI_IFACE_NAME = "wlan0";
private static final String TEST_COUNTRY_CODE = "US";
@@ -202,6 +204,7 @@ public class WifiServiceImplTest {
@Mock WifiTrafficPoller mWifiTrafficPolller;
@Mock ScanRequestProxy mScanRequestProxy;
@Mock ITrafficStateCallback mTrafficStateCallback;
+ @Mock INetworkRequestMatchCallback mNetworkRequestMatchCallback;
@Spy FakeWifiLog mLog;
@@ -3009,4 +3012,82 @@ public class WifiServiceImplTest {
mLooper.dispatchAll();
verify(mWifiTrafficPoller).removeCallback(0);
}
+
+ /**
+ * Verify that a call to registerNetworkRequestMatchCallback throws a SecurityException if the
+ * caller does not have NETWORK_SETTINGS permission.
+ */
+ @Test
+ public void registerNetworkRequestMatchCallbackThrowsSecurityExceptionOnMissingPermissions() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ try {
+ mWifiServiceImpl.registerNetworkRequestMatchCallback(mAppBinder,
+ mNetworkRequestMatchCallback,
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ fail("expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Verify that a call to registerNetworkRequestMatchCallback throws an IllegalArgumentException
+ * if the parameters are not provided.
+ */
+ @Test
+ public void
+ registerNetworkRequestMatchCallbackThrowsIllegalArgumentExceptionOnInvalidArguments() {
+ try {
+ mWifiServiceImpl.registerNetworkRequestMatchCallback(
+ mAppBinder, null, TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /**
+ * Verify that a call to unregisterNetworkRequestMatchCallback throws a SecurityException if the
+ * caller does not have NETWORK_SETTINGS permission.
+ */
+ @Test
+ public void unregisterNetworkRequestMatchCallbackThrowsSecurityExceptionOnMissingPermissions() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ try {
+ mWifiServiceImpl.unregisterNetworkRequestMatchCallback(
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ fail("expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Verify that registerNetworkRequestMatchCallback adds callback to
+ * {@link ClientModeImpl}.
+ */
+ @Test
+ public void registerNetworkRequestMatchCallbackAndVerify() throws Exception {
+ mWifiServiceImpl.registerNetworkRequestMatchCallback(
+ mAppBinder, mNetworkRequestMatchCallback,
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ mLooper.dispatchAll();
+ verify(mClientModeImpl).addNetworkRequestMatchCallback(
+ mAppBinder, mNetworkRequestMatchCallback,
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ }
+
+ /**
+ * Verify that unregisterNetworkRequestMatchCallback removes callback from
+ * {@link ClientModeImpl}.
+ */
+ @Test
+ public void unregisterNetworkRequestMatchCallbackAndVerify() throws Exception {
+ mWifiServiceImpl.unregisterNetworkRequestMatchCallback(
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ mLooper.dispatchAll();
+ verify(mClientModeImpl).removeNetworkRequestMatchCallback(
+ TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER);
+ }
}