Merge tag 'android-security-10.0.0_r51' into int/10/fp2
Android security 10.0.0 release 51
* tag 'android-security-10.0.0_r51':
WifiConfigManager: protect CONFIGURED_NETWORKS_CHANGED_ACTION with permissions
Change-Id: I804bc397dab23abb37e7bffe419a5964663e6351
diff --git a/service/java/com/android/server/wifi/BaseWifiDiagnostics.java b/service/java/com/android/server/wifi/BaseWifiDiagnostics.java
index 2090cac..3abf510 100644
--- a/service/java/com/android/server/wifi/BaseWifiDiagnostics.java
+++ b/service/java/com/android/server/wifi/BaseWifiDiagnostics.java
@@ -1,6 +1,8 @@
package com.android.server.wifi;
+import android.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -23,7 +25,11 @@
mWifiNative = wifiNative;
}
- public synchronized void startLogging(boolean verboseEnabled) {
+ /**
+ * start wifi HAL dependent logging features
+ * @param ifaceName requesting to start logging
+ */
+ public synchronized void startLogging(@NonNull String ifaceName) {
mFirmwareVersion = mWifiNative.getFirmwareVersion();
mDriverVersion = mWifiNative.getDriverVersion();
mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet();
@@ -33,7 +39,11 @@
public synchronized void stopPacketLog() { }
- public synchronized void stopLogging() { }
+ /**
+ * stop wifi HAL dependent logging features
+ * @param ifaceName requesting to stop logging
+ */
+ public synchronized void stopLogging(@NonNull String ifaceName) { }
/**
* Inform the diagnostics module of a connection event.
@@ -64,4 +74,11 @@
pw.println("Driver Version is: " + mDriverVersion);
pw.println("Supported Feature set: " + mSupportedFeatureSet);
}
-}
\ No newline at end of file
+
+ /** enables/disables wifi verbose logging */
+ public synchronized void enableVerboseLogging(boolean verboseEnabled) { }
+
+ /** enables packet fate monitoring */
+ public void startPktFateMonitoring(@NonNull String ifaceName) {}
+
+}
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index d98d022..5cf9096 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -163,7 +163,7 @@
private static final String EXTRA_UID = "uid";
private static final String EXTRA_PACKAGE_NAME = "PackageName";
private static final String EXTRA_PASSPOINT_CONFIGURATION = "PasspointConfiguration";
- private static final int IPCLIENT_TIMEOUT_MS = 10_000;
+ private static final int IPCLIENT_TIMEOUT_MS = 60_000;
private boolean mVerboseLoggingEnabled = false;
private final WifiPermissionsWrapper mWifiPermissionsWrapper;
@@ -763,8 +763,14 @@
private WifiStateTracker mWifiStateTracker;
private final BackupManagerProxy mBackupManagerProxy;
private final WrongPasswordNotifier mWrongPasswordNotifier;
+ private final ConnectionFailureNotifier mConnectionFailureNotifier;
private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
private boolean mConnectedMacRandomzationSupported;
+ // Maximum duration to continue to log Wifi usability stats after a data stall is triggered.
+ @VisibleForTesting
+ public static final long DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS = 30 * 1000;
+ private long mDataStallTriggerTimeMs = -1;
+ private int mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN;
public ClientModeImpl(Context context, FrameworkFacade facade, Looper looper,
UserManager userManager, WifiInjector wifiInjector,
@@ -811,7 +817,8 @@
mSupplicantStateTracker =
mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this);
-
+ mConnectionFailureNotifier = mWifiInjector.makeConnectionFailureNotifier(
+ mWifiConnectivityManager);
mLinkProperties = new LinkProperties();
mMcastLockManagerFilterController = new McastLockManagerFilterController();
@@ -959,6 +966,8 @@
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID,
mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT,
+ mWifiInjector.getWifiLastResortWatchdog().getHandler());
}
private void setMulticastFilter(boolean enabled) {
@@ -1106,7 +1115,7 @@
setSupplicantLogLevel();
mCountryCode.enableVerboseLogging(verbose);
mWifiScoreReport.enableVerboseLogging(mVerboseLoggingEnabled);
- mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);
+ mWifiDiagnostics.enableVerboseLogging(mVerboseLoggingEnabled);
mWifiMonitor.enableVerboseLogging(verbose);
mWifiNative.enableVerboseLogging(verbose);
mWifiConfigManager.enableVerboseLogging(verbose);
@@ -1773,12 +1782,14 @@
* Remove a Passpoint configuration synchronously.
*
* @param channel Channel for communicating with the state machine
+ * @param privileged Whether the caller is a privileged entity
* @param fqdn The FQDN of the Passpoint configuration to remove
* @return true on success
*/
- public boolean syncRemovePasspointConfig(AsyncChannel channel, String fqdn) {
+ public boolean syncRemovePasspointConfig(AsyncChannel channel, boolean privileged,
+ String fqdn) {
Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_PASSPOINT_CONFIG,
- fqdn);
+ privileged ? 1 : 0, 0, fqdn);
if (messageIsNull(resultMsg)) return false;
boolean result = (resultMsg.arg1 == SUCCESS);
resultMsg.recycle();
@@ -1789,10 +1800,13 @@
* Get the list of installed Passpoint configurations synchronously.
*
* @param channel Channel for communicating with the state machine
+ * @param privileged Whether the caller is a privileged entity
* @return List of {@link PasspointConfiguration}
*/
- public List<PasspointConfiguration> syncGetPasspointConfigs(AsyncChannel channel) {
- Message resultMsg = channel.sendMessageSynchronously(CMD_GET_PASSPOINT_CONFIGS);
+ public List<PasspointConfiguration> syncGetPasspointConfigs(AsyncChannel channel,
+ boolean privileged) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_GET_PASSPOINT_CONFIGS,
+ privileged ? 1 : 0);
if (messageIsNull(resultMsg)) return null;
List<PasspointConfiguration> result = (List<PasspointConfiguration>) resultMsg.obj;
resultMsg.recycle();
@@ -3146,6 +3160,16 @@
mWifiScoreCard.noteConnectionFailure(mWifiInfo,
level2FailureCode, connectivityFailureCode);
}
+ boolean isAssociationRejection = level2FailureCode
+ == WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION;
+ boolean isAuthenticationFailure = level2FailureCode
+ == WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE
+ && level2FailureReason != WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD;
+ if ((isAssociationRejection || isAuthenticationFailure)
+ && mWifiConfigManager.isInFlakyRandomizationSsidHotlist(mTargetNetworkId)) {
+ mConnectionFailureNotifier
+ .showFailedToConnectDueToNoRandomizedMacSupportNotification(mTargetNetworkId);
+ }
// if connected, this should be non-null.
WifiConfiguration configuration = getCurrentWifiConfiguration();
if (configuration == null) {
@@ -3373,12 +3397,14 @@
Log.e(TAG, "No config to change MAC address to");
return;
}
- MacAddress currentMac = MacAddress.fromString(mWifiNative.getMacAddress(mInterfaceName));
+ String currentMacString = mWifiNative.getMacAddress(mInterfaceName);
+ MacAddress currentMac = currentMacString == null ? null :
+ MacAddress.fromString(currentMacString);
MacAddress newMac = config.getOrCreateRandomizedMacAddress();
mWifiConfigManager.setNetworkRandomizedMacAddress(config.networkId, newMac);
if (!WifiConfiguration.isValidMacAddressForRandomization(newMac)) {
Log.wtf(TAG, "Config generated an invalid MAC address");
- } else if (currentMac.equals(newMac)) {
+ } else if (newMac.equals(currentMac)) {
Log.d(TAG, "No changes in MAC address");
} else {
mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config);
@@ -3386,7 +3412,7 @@
mWifiNative.setMacAddress(mInterfaceName, newMac);
Log.d(TAG, "ConnectedMacRandomization SSID(" + config.getPrintableSsid()
+ "). setMacAddress(" + newMac.toString() + ") from "
- + currentMac.toString() + " = " + setMacSuccess);
+ + currentMacString + " = " + setMacSuccess);
}
}
@@ -3678,11 +3704,13 @@
break;
case CMD_REMOVE_PASSPOINT_CONFIG:
int removeResult = mPasspointManager.removeProvider(
- (String) message.obj) ? SUCCESS : FAILURE;
+ message.sendingUid, message.arg1 == 1, (String) message.obj)
+ ? SUCCESS : FAILURE;
replyToMessage(message, message.what, removeResult);
break;
case CMD_GET_PASSPOINT_CONFIGS:
- replyToMessage(message, message.what, mPasspointManager.getProviderConfigs());
+ replyToMessage(message, message.what, mPasspointManager.getProviderConfigs(
+ message.sendingUid, message.arg1 == 1));
break;
case CMD_RESET_SIM_NETWORKS:
/* Defer this message until supplicant is started. */
@@ -3765,7 +3793,9 @@
setRandomMacOui();
mCountryCode.setReadyForChange(true);
- mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);
+ mWifiDiagnostics.startPktFateMonitoring(mInterfaceName);
+ mWifiDiagnostics.startLogging(mInterfaceName);
+
mIsRunning = true;
updateBatteryWorkSource(null);
@@ -3803,7 +3833,7 @@
*/
private void stopClientMode() {
// exiting supplicant started state is now only applicable to client mode
- mWifiDiagnostics.stopLogging();
+ mWifiDiagnostics.stopLogging(mInterfaceName);
mIsRunning = false;
updateBatteryWorkSource(null);
@@ -4287,10 +4317,12 @@
&& TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) {
String anonAtRealm = TelephonyUtil.getAnonymousIdentityWith3GppRealm(
getTelephonyManager());
+ // Use anonymous@<realm> when pseudonym is not available
config.enterpriseConfig.setAnonymousIdentity(anonAtRealm);
}
if (mWifiNative.connectToNetwork(mInterfaceName, config)) {
+ mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime();
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
mTargetWifiConfiguration = config;
@@ -4450,20 +4482,27 @@
// We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
if (config.enterpriseConfig != null
&& TelephonyUtil.isSimEapMethod(
- config.enterpriseConfig.getEapMethod())
- // if using anonymous@<realm>, do not use pseudonym identity on
- // reauthentication. Instead, use full authentication using
- // anonymous@<realm> followed by encrypted IMSI every time.
- // This is because the encrypted IMSI spec does not specify its
- // compatibility with the pseudonym identity specified by EAP-AKA.
- && !TelephonyUtil.isAnonymousAtRealmIdentity(
- config.enterpriseConfig.getAnonymousIdentity())) {
+ config.enterpriseConfig.getEapMethod())) {
String anonymousIdentity =
mWifiNative.getEapAnonymousIdentity(mInterfaceName);
- if (mVerboseLoggingEnabled) {
- log("EAP Pseudonym: " + anonymousIdentity);
+ if (!TextUtils.isEmpty(anonymousIdentity)
+ && !TelephonyUtil
+ .isAnonymousAtRealmIdentity(anonymousIdentity)) {
+ String decoratedPseudonym = TelephonyUtil
+ .decoratePseudonymWith3GppRealm(getTelephonyManager(),
+ anonymousIdentity);
+ if (decoratedPseudonym != null) {
+ anonymousIdentity = decoratedPseudonym;
+ }
+ if (mVerboseLoggingEnabled) {
+ log("EAP Pseudonym: " + anonymousIdentity);
+ }
+ // Save the pseudonym only if it is a real one
+ config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
+ } else {
+ // Clear any stored pseudonyms
+ config.enterpriseConfig.setAnonymousIdentity(null);
}
- config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
}
sendNetworkStateChangeBroadcast(mLastBssid);
@@ -4517,7 +4556,8 @@
break;
case CMD_REMOVE_PASSPOINT_CONFIG:
String fqdn = (String) message.obj;
- if (mPasspointManager.removeProvider(fqdn)) {
+ if (mPasspointManager.removeProvider(
+ message.sendingUid, message.arg1 == 1, fqdn)) {
if (isProviderOwnedNetwork(mTargetNetworkId, fqdn)
|| isProviderOwnedNetwork(mLastNetworkId, fqdn)) {
logd("Disconnect from current network since its provider is removed");
@@ -5107,11 +5147,24 @@
}
mWifiScoreReport.noteIpCheck();
}
- int statusDataStall =
- mWifiDataStall.checkForDataStall(mLastLinkLayerStats, stats);
- if (statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) {
- mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
- convertToUsabilityStatsTriggerType(statusDataStall), -1);
+ int statusDataStall = mWifiDataStall.checkForDataStall(
+ mLastLinkLayerStats, stats, mWifiInfo);
+ if (mDataStallTriggerTimeMs == -1
+ && statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) {
+ mDataStallTriggerTimeMs = mClock.getElapsedSinceBootMillis();
+ mLastStatusDataStall = statusDataStall;
+ }
+ if (mDataStallTriggerTimeMs != -1) {
+ long elapsedTime = mClock.getElapsedSinceBootMillis()
+ - mDataStallTriggerTimeMs;
+ if (elapsedTime >= DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS) {
+ mDataStallTriggerTimeMs = -1;
+ mWifiMetrics.addToWifiUsabilityStatsList(
+ WifiUsabilityStats.LABEL_BAD,
+ convertToUsabilityStatsTriggerType(mLastStatusDataStall),
+ -1);
+ mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN;
+ }
}
mWifiMetrics.incrementWifiLinkLayerUsageStats(stats);
mLastLinkLayerStats = stats;
@@ -5341,7 +5394,6 @@
.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
.withNetwork(getCurrentNetwork())
.withDisplayName(currentConfig.SSID)
- .withRandomMacAddress()
.build();
} else {
StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
diff --git a/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java
new file mode 100644
index 0000000..f4f89f0
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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 android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.os.Handler;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
+
+/**
+ * Helper class for ConnectionFailureNotifier.
+ */
+public class ConnectionFailureNotificationBuilder {
+ private static final String TAG = "ConnectionFailureNotifier";
+
+ public static final String ACTION_SHOW_SET_RANDOMIZATION_DETAILS =
+ "com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS";
+ public static final String RANDOMIZATION_SETTINGS_NETWORK_ID =
+ "com.android.server.wifi.RANDOMIZATION_SETTINGS_NETWORK_ID";
+ public static final String RANDOMIZATION_SETTINGS_NETWORK_SSID =
+ "com.android.server.wifi.RANDOMIZATION_SETTINGS_NETWORK_SSID";
+
+ private Context mContext;
+ private String mPackageName;
+ private Resources mResources;
+ private FrameworkFacade mFrameworkFacade;
+ private WifiConnectivityManager mWifiConnectivityManager;
+ private NotificationManager mNotificationManager;
+ private Handler mHandler;
+
+ public ConnectionFailureNotificationBuilder(Context context, String packageName,
+ FrameworkFacade framework) {
+ mContext = context;
+ mPackageName = packageName;
+ mResources = context.getResources();
+ mFrameworkFacade = framework;
+ }
+
+ /**
+ * Creates a notification that alerts the user that the connection may be failing due to
+ * MAC randomization.
+ * @param config
+ */
+ public Notification buildNoMacRandomizationSupportNotification(
+ @NonNull WifiConfiguration config) {
+ String ssid = config.SSID;
+ String ssidAndSecurityType = config.getSsidAndSecurityTypeString();
+ String title = mResources.getString(
+ R.string.wifi_cannot_connect_with_randomized_mac_title, ssid);
+ String content = mResources.getString(
+ R.string.wifi_cannot_connect_with_randomized_mac_message);
+
+ Intent showDetailIntent = new Intent(ACTION_SHOW_SET_RANDOMIZATION_DETAILS)
+ .setPackage(mPackageName);
+ showDetailIntent.putExtra(RANDOMIZATION_SETTINGS_NETWORK_ID, config.networkId);
+ showDetailIntent.putExtra(RANDOMIZATION_SETTINGS_NETWORK_SSID, ssidAndSecurityType);
+ PendingIntent pendingShowDetailIntent = mFrameworkFacade.getBroadcast(
+ mContext, 0, showDetailIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return mFrameworkFacade.makeNotificationBuilder(mContext,
+ SystemNotificationChannels.NETWORK_ALERTS)
+ .setSmallIcon(R.drawable.stat_notify_wifi_in_range)
+ .setTicker(title)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setContentIntent(pendingShowDetailIntent)
+ .setShowWhen(false)
+ .setLocalOnly(true)
+ .setColor(mResources.getColor(R.color.system_notification_accent_color,
+ mContext.getTheme()))
+ .setAutoCancel(true)
+ .build();
+ }
+
+ /**
+ * Creates an AlertDialog that allows the user to disable MAC randomization for a network.
+ * @param ssid the displayed SSID in the dialog
+ * @param onUserConfirm
+ */
+ public AlertDialog buildChangeMacRandomizationSettingDialog(
+ String ssid, DialogInterface.OnClickListener onUserConfirm) {
+ AlertDialog.Builder builder = mFrameworkFacade.makeAlertDialogBuilder(mContext)
+ .setTitle(mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_title))
+ .setMessage(mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_message, ssid))
+ .setPositiveButton(
+ mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_confirm_text),
+ onUserConfirm)
+ // A null listener allows the dialog to be dismissed directly.
+ .setNegativeButton(R.string.no, null);
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ return dialog;
+ }
+}
diff --git a/service/java/com/android/server/wifi/ConnectionFailureNotifier.java b/service/java/com/android/server/wifi/ConnectionFailureNotifier.java
new file mode 100644
index 0000000..bbef2ff
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConnectionFailureNotifier.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 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 android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.os.Handler;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.internal.R;
+
+/**
+ * This class may be used to launch notifications when wifi connections fail.
+ */
+public class ConnectionFailureNotifier {
+ private static final String TAG = "ConnectionFailureNotifier";
+ public static final int NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID = 123;
+
+ private Context mContext;
+ private WifiInjector mWifiInjector;
+ private Resources mResources;
+ private FrameworkFacade mFrameworkFacade;
+ private WifiConfigManager mWifiConfigManager;
+ private WifiConnectivityManager mWifiConnectivityManager;
+ private NotificationManager mNotificationManager;
+ private Handler mHandler;
+ private ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
+
+ public ConnectionFailureNotifier(
+ Context context, WifiInjector wifiInjector, FrameworkFacade framework,
+ WifiConfigManager wifiConfigManager, WifiConnectivityManager wifiConnectivityManager,
+ Handler handler) {
+ mContext = context;
+ mWifiInjector = wifiInjector;
+ mResources = context.getResources();
+ mFrameworkFacade = framework;
+ mWifiConfigManager = wifiConfigManager;
+ mWifiConnectivityManager = wifiConnectivityManager;
+ mNotificationManager = mWifiInjector.getNotificationManager();
+ mHandler = handler;
+ mConnectionFailureNotificationBuilder =
+ mWifiInjector.getConnectionFailureNotificationBuilder();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectionFailureNotificationBuilder
+ .ACTION_SHOW_SET_RANDOMIZATION_DETAILS);
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ConnectionFailureNotificationBuilder
+ .ACTION_SHOW_SET_RANDOMIZATION_DETAILS)) {
+ int networkId = intent.getIntExtra(
+ ConnectionFailureNotificationBuilder
+ .RANDOMIZATION_SETTINGS_NETWORK_ID,
+ WifiConfiguration.INVALID_NETWORK_ID);
+ String ssidAndSecurityType = intent.getStringExtra(
+ ConnectionFailureNotificationBuilder
+ .RANDOMIZATION_SETTINGS_NETWORK_SSID);
+ showRandomizationSettingsDialog(networkId, ssidAndSecurityType);
+ }
+ }
+ }, filter);
+ }
+
+ /**
+ * Shows a notification which will bring up a dialog which offers the user an option to disable
+ * MAC randomization on |networkdId|.
+ * @param networkId
+ */
+ public void showFailedToConnectDueToNoRandomizedMacSupportNotification(int networkId) {
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
+ if (config == null) {
+ return;
+ }
+ Notification notification = mConnectionFailureNotificationBuilder
+ .buildNoMacRandomizationSupportNotification(config);
+ mNotificationManager.notify(NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID, notification);
+ }
+
+ class DisableMacRandomizationListener implements DialogInterface.OnClickListener {
+ private WifiConfiguration mConfig;
+
+ DisableMacRandomizationListener(WifiConfiguration config) {
+ mConfig = config;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mHandler.post(() -> {
+ mConfig.macRandomizationSetting =
+ WifiConfiguration.RANDOMIZATION_NONE;
+ mWifiConfigManager.addOrUpdateNetwork(mConfig, Process.SYSTEM_UID);
+ WifiConfiguration updatedConfig =
+ mWifiConfigManager.getConfiguredNetwork(mConfig.networkId);
+ if (updatedConfig.macRandomizationSetting
+ == WifiConfiguration.RANDOMIZATION_NONE) {
+ String message = mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_success);
+ mFrameworkFacade.showToast(mContext, message);
+ mWifiConfigManager.enableNetwork(updatedConfig.networkId, true,
+ Process.SYSTEM_UID);
+ mWifiConnectivityManager.forceConnectivityScan(
+ ClientModeImpl.WIFI_WORK_SOURCE);
+ } else {
+ // Shouldn't ever fail, but here for completeness
+ String message = mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_failure);
+ mFrameworkFacade.showToast(mContext, message);
+ Log.e(TAG, "Failed to modify mac randomization setting");
+ }
+ });
+ }
+ }
+
+ /**
+ * Class to show a AlertDialog which notifies the user of a network not being privacy
+ * compliant and then suggests an action.
+ */
+ private void showRandomizationSettingsDialog(int networkId, String ssidAndSecurityType) {
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
+ // Make sure the networkId is still pointing to the correct WifiConfiguration since
+ // there might be a large time gap between when the notification shows and when
+ // it's tapped.
+ if (config == null || ssidAndSecurityType == null
+ || !ssidAndSecurityType.equals(config.getSsidAndSecurityTypeString())) {
+ String message = mResources.getString(
+ R.string.wifi_disable_mac_randomization_dialog_network_not_found);
+ mFrameworkFacade.showToast(mContext, message);
+ return;
+ }
+
+ AlertDialog dialog = mConnectionFailureNotificationBuilder
+ .buildChangeMacRandomizationSettingDialog(config.SSID,
+ new DisableMacRandomizationListener(config));
+ dialog.show();
+ }
+}
diff --git a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
index 0c06488..b71d5a0 100644
--- a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
+++ b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
@@ -16,6 +16,9 @@
package com.android.server.wifi;
+import android.annotation.Nullable;
+
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -44,7 +47,8 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (mSsidToTimeMap != null) {
XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidToTimeMap);
@@ -52,7 +56,9 @@
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
diff --git a/service/java/com/android/server/wifi/DeviceConfigFacade.java b/service/java/com/android/server/wifi/DeviceConfigFacade.java
new file mode 100644
index 0000000..25cc2f7
--- /dev/null
+++ b/service/java/com/android/server/wifi/DeviceConfigFacade.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 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 android.provider.DeviceConfig;
+import android.util.ArraySet;
+
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class allows getting all configurable flags from DeviceConfig.
+ */
+public class DeviceConfigFacade {
+ private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS =
+ (int) TimeUnit.SECONDS.toMillis(30);
+ private static final String NAMESPACE = "wifi";
+ // Default duration for evaluating Wifi condition to trigger a data stall
+ // measured in milliseconds
+ public static final int DEFAULT_DATA_STALL_DURATION_MS = 1500;
+ // Default threshold of Tx throughput below which to trigger a data stall measured in Mbps
+ public static final int DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS = 2;
+ // Default threshold of Rx throughput below which to trigger a data stall measured in Mbps
+ public static final int DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS = 2;
+ // Default threshold of Tx packet error rate above which to trigger a data stall in percentage
+ public static final int DEFAULT_DATA_STALL_TX_PER_THR = 90;
+ // Default threshold of CCA level above which to trigger a data stall in percentage
+ public static final int DEFAULT_DATA_STALL_CCA_LEVEL_THR = 100;
+
+ /**
+ * Gets the feature flag for reporting abnormally long connections.
+ */
+ public boolean isAbnormalConnectionBugreportEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE, "abnormal_connection_bugreport_enabled", false);
+ }
+
+ /**
+ * Gets the threshold for classifying abnormally long connections.
+ */
+ public int getAbnormalConnectionDurationMs() {
+ return DeviceConfig.getInt(NAMESPACE, "abnormal_connection_duration_ms",
+ DEFAULT_ABNORMAL_CONNECTION_DURATION_MS);
+ }
+
+ /**
+ * Adds a listener that will be run on the specified executor.
+ * @param executor
+ * @param onPropertiesChangedListener
+ */
+ public void addOnPropertiesChangedListener(Executor executor,
+ DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener) {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor,
+ onPropertiesChangedListener);
+ }
+
+ /**
+ * Gets the duration of evaluating Wifi condition to trigger a data stall.
+ */
+ public int getDataStallDurationMs() {
+ return DeviceConfig.getInt(NAMESPACE, "data_stall_duration_ms",
+ DEFAULT_DATA_STALL_DURATION_MS);
+ }
+
+ /**
+ * Gets the threshold of Tx throughput below which to trigger a data stall.
+ */
+ public int getDataStallTxTputThrMbps() {
+ return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_tput_thr_mbps",
+ DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS);
+ }
+
+ /**
+ * Gets the threshold of Rx throughput below which to trigger a data stall.
+ */
+ public int getDataStallRxTputThrMbps() {
+ return DeviceConfig.getInt(NAMESPACE, "data_stall_rx_tput_thr_mbps",
+ DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS);
+ }
+
+ /**
+ * Gets the threshold of Tx packet error rate above which to trigger a data stall.
+ */
+ public int getDataStallTxPerThr() {
+ return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_per_thr",
+ DEFAULT_DATA_STALL_TX_PER_THR);
+ }
+
+ /**
+ * Gets the threshold of CCA level above which to trigger a data stall.
+ */
+ public int getDataStallCcaLevelThr() {
+ return DeviceConfig.getInt(NAMESPACE, "data_stall_cca_level_thr",
+ DEFAULT_DATA_STALL_CCA_LEVEL_THR);
+ }
+
+ /**
+ * Gets the Set of SSIDs in the flaky SSID hotlist.
+ */
+ public Set<String> getRandomizationFlakySsidHotlist() {
+ String ssidHotlist = DeviceConfig.getString(NAMESPACE,
+ "randomization_flaky_ssid_hotlist", "");
+ Set<String> result = new ArraySet<String>();
+ String[] ssidHotlistArray = ssidHotlist.split(",");
+ for (int i = 0; i < ssidHotlistArray.length; i++) {
+ String cur = ssidHotlistArray[i];
+ if (cur.length() == 0) {
+ continue;
+ }
+ // Make sure the SSIDs are quoted. Server side should not quote ssids.
+ result.add("\"" + cur + "\"");
+ }
+ return result;
+ }
+}
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index f3c5d4b..fe3027e 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -17,6 +17,7 @@
package com.android.server.wifi;
import android.app.ActivityManagerInternal;
+import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.Notification;
import android.app.PendingIntent;
@@ -35,6 +36,7 @@
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.widget.Toast;
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
@@ -45,6 +47,11 @@
*/
public class FrameworkFacade {
public static final String TAG = "FrameworkFacade";
+ /**
+ * NIAP global settings flag.
+ * Note: This should be added to {@link android.provider.Settings.Global}.
+ */
+ private static final String NIAP_MODE_SETTINGS_NAME = "niap_mode";
private ActivityManagerInternal mActivityManagerInternal;
@@ -83,6 +90,13 @@
}
/**
+ * Returns whether the device is in NIAP mode or not.
+ */
+ public boolean isNiapModeOn(Context context) {
+ return getIntegerSetting(context, NIAP_MODE_SETTINGS_NAME, 0) == 1;
+ }
+
+ /**
* Helper method for classes to register a ContentObserver
* {@see ContentResolver#registerContentObserver(Uri,boolean,ContentObserver)}.
*
@@ -215,4 +229,23 @@
public Notification.Builder makeNotificationBuilder(Context context, String channelId) {
return new Notification.Builder(context, channelId);
}
+
+ /**
+ * Create a new instance of {@link AlertDialog.Builder}.
+ * @param context reference to a Context
+ * @return an instance of AlertDialog.Builder
+ */
+ public AlertDialog.Builder makeAlertDialogBuilder(Context context) {
+ return new AlertDialog.Builder(context);
+ }
+
+ /**
+ * Show a toast message
+ * @param context reference to a Context
+ * @param text the message to display
+ */
+ public void showToast(Context context, String text) {
+ Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
+ toast.show();
+ }
}
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index bb53a6e..e10234f 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -37,6 +37,7 @@
import android.os.Handler;
import android.os.HidlSupport.Mutable;
import android.os.HwRemoteBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.LongSparseArray;
@@ -73,13 +74,19 @@
public static final int START_HAL_RETRY_TIMES = 3;
private final Clock mClock;
+ private final Handler mEventHandler;
+ private WifiDeathRecipient mIWifiDeathRecipient;
+ private ServiceManagerDeathRecipient mServiceManagerDeathRecipient;
// cache the value for supporting vendor HAL or not
private boolean mIsVendorHalSupported = false;
// public API
- public HalDeviceManager(Clock clock) {
+ public HalDeviceManager(Clock clock, Looper looper) {
mClock = clock;
+ mEventHandler = new Handler(looper);
+ mIWifiDeathRecipient = new WifiDeathRecipient();
+ mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient();
mInterfaceAvailableForRequestListeners.put(IfaceType.STA, new HashMap<>());
mInterfaceAvailableForRequestListeners.put(IfaceType.AP, new HashMap<>());
@@ -631,15 +638,19 @@
mRttControllerLifecycleCallbacks.clear();
}
- private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
- cookie -> {
+ private class ServiceManagerDeathRecipient implements HwRemoteBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ mEventHandler.post(() -> {
Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
synchronized (mLock) {
mServiceManager = null;
// theoretically can call initServiceManager again here - but
// there's no point since most likely system is going to reboot
}
- };
+ });
+ }
+ }
private final IServiceNotification mServiceNotificationCallback =
new IServiceNotification.Stub() {
@@ -718,8 +729,10 @@
}
}
- private final HwRemoteBinder.DeathRecipient mIWifiDeathRecipient =
- cookie -> {
+ private class WifiDeathRecipient implements HwRemoteBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ mEventHandler.post(() -> {
Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie=" + cookie);
synchronized (mLock) { // prevents race condition with surrounding method
mWifi = null;
@@ -727,7 +740,9 @@
teardownInternal();
// don't restart: wait for registration notification
}
- };
+ });
+ }
+ }
/**
* Initialize IWifi and register death listener and event callback.
@@ -1264,21 +1279,26 @@
private class WifiEventCallback extends IWifiEventCallback.Stub {
@Override
public void onStart() throws RemoteException {
- if (VDBG) Log.d(TAG, "IWifiEventCallback.onStart");
- // NOP: only happens in reaction to my calls - will handle directly
+ mEventHandler.post(() -> {
+ if (VDBG) Log.d(TAG, "IWifiEventCallback.onStart");
+ // NOP: only happens in reaction to my calls - will handle directly
+ });
}
@Override
public void onStop() throws RemoteException {
- if (VDBG) Log.d(TAG, "IWifiEventCallback.onStop");
- // NOP: only happens in reaction to my calls - will handle directly
+ mEventHandler.post(() -> {
+ if (VDBG) Log.d(TAG, "IWifiEventCallback.onStop");
+ // NOP: only happens in reaction to my calls - will handle directly
+ });
}
@Override
public void onFailure(WifiStatus status) throws RemoteException {
- Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
- teardownInternal();
-
+ mEventHandler.post(() -> {
+ Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
+ teardownInternal();
+ });
// No need to do anything else: listeners may (will) re-start Wi-Fi
}
}
@@ -1703,9 +1723,11 @@
int requestedIfaceType, WifiIfaceInfo[][] currentIfaces, int numNecessaryInterfaces) {
// rule 0: check for any low priority interfaces
int numAvailableLowPriorityInterfaces = 0;
- for (InterfaceCacheEntry entry : mInterfaceInfoCache.values()) {
- if (entry.type == existingIfaceType && entry.isLowPriority) {
- numAvailableLowPriorityInterfaces++;
+ synchronized (mLock) {
+ for (InterfaceCacheEntry entry : mInterfaceInfoCache.values()) {
+ if (entry.type == existingIfaceType && entry.isLowPriority) {
+ numAvailableLowPriorityInterfaces++;
+ }
}
}
if (numAvailableLowPriorityInterfaces >= numNecessaryInterfaces) {
@@ -1760,8 +1782,10 @@
LongSparseArray<WifiIfaceInfo> orderedListLowPriority = new LongSparseArray<>();
LongSparseArray<WifiIfaceInfo> orderedList = new LongSparseArray<>();
for (WifiIfaceInfo info : interfaces) {
- InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(
- Pair.create(info.name, getType(info.iface)));
+ InterfaceCacheEntry cacheEntry;
+ synchronized (mLock) {
+ cacheEntry = mInterfaceInfoCache.get(Pair.create(info.name, getType(info.iface)));
+ }
if (cacheEntry == null) {
Log.e(TAG,
"selectInterfacesToDelete: can't find cache entry with name=" + info.name);
diff --git a/service/java/com/android/server/wifi/HostapdHal.java b/service/java/com/android/server/wifi/HostapdHal.java
index 69c787f..5ac1734 100644
--- a/service/java/com/android/server/wifi/HostapdHal.java
+++ b/service/java/com/android/server/wifi/HostapdHal.java
@@ -33,12 +33,16 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.WifiNative.HostapdDeathEventHandler;
+import com.android.server.wifi.util.ApConfigUtil;
import com.android.server.wifi.util.NativeUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.ThreadSafe;
@@ -51,6 +55,8 @@
private static final String TAG = "HostapdHal";
@VisibleForTesting
public static final String HAL_INSTANCE_NAME = "default";
+ @VisibleForTesting
+ public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L;
private final Object mLock = new Object();
private boolean mVerboseLoggingEnabled = false;
@@ -59,6 +65,8 @@
private final boolean mEnableIeee80211AC;
private final List<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange>
mAcsChannelRanges;
+ private boolean mForceApChannel = false;
+ private int mForcedApChannel;
// Hostapd HAL interface objects
private IServiceManager mIServiceManager = null;
@@ -230,11 +238,11 @@
* Link to death for IHostapd object.
* @return true on success, false otherwise.
*/
- private boolean linkToHostapdDeath() {
+ private boolean linkToHostapdDeath(HwRemoteBinder.DeathRecipient deathRecipient, long cookie) {
synchronized (mLock) {
if (mIHostapd == null) return false;
try {
- if (!mIHostapd.linkToDeath(mHostapdDeathRecipient, ++mDeathRecipientCookie)) {
+ if (!mIHostapd.linkToDeath(deathRecipient, cookie)) {
Log.wtf(TAG, "Error on linkToDeath on IHostapd");
hostapdServiceDiedHandler(mDeathRecipientCookie);
return false;
@@ -282,7 +290,7 @@
Log.e(TAG, "Got null IHostapd service. Stopping hostapd HIDL startup");
return false;
}
- if (!linkToHostapdDeath()) {
+ if (!linkToHostapdDeath(mHostapdDeathRecipient, ++mDeathRecipientCookie)) {
mIHostapd = null;
return false;
}
@@ -296,6 +304,22 @@
}
/**
+ * Enable force-soft-AP-channel mode which takes effect when soft AP starts next time
+ * @param forcedApChannel The forced IEEE channel number
+ */
+ void enableForceSoftApChannel(int forcedApChannel) {
+ mForceApChannel = true;
+ mForcedApChannel = forcedApChannel;
+ }
+
+ /**
+ * Disable force-soft-AP-channel mode which take effect when soft AP starts next time
+ */
+ void disableForceSoftApChannel() {
+ mForceApChannel = false;
+ }
+
+ /**
* Add and start a new access point.
*
* @param ifaceName Name of the interface.
@@ -317,7 +341,15 @@
Log.e(TAG, "Unrecognized apBand " + config.apBand);
return false;
}
- if (mEnableAcs) {
+ if (mForceApChannel) {
+ ifaceParams.channelParams.enableAcs = false;
+ ifaceParams.channelParams.channel = mForcedApChannel;
+ if (mForcedApChannel <= ApConfigUtil.HIGHEST_2G_AP_CHANNEL) {
+ ifaceParams.channelParams.band = IHostapd.Band.BAND_2_4_GHZ;
+ } else {
+ ifaceParams.channelParams.band = IHostapd.Band.BAND_5_GHZ;
+ }
+ } else if (mEnableAcs) {
ifaceParams.channelParams.enableAcs = true;
ifaceParams.channelParams.acsShouldExcludeDfs = true;
} else {
@@ -486,10 +518,19 @@
}
/**
- * Terminate the hostapd daemon.
+ * Terminate the hostapd daemon & wait for it's death.
*/
public void terminate() {
synchronized (mLock) {
+ // Register for a new death listener to block until hostapd is dead.
+ final long waitForDeathCookie = new Random().nextLong();
+ final CountDownLatch waitForDeathLatch = new CountDownLatch(1);
+ linkToHostapdDeath((cookie) -> {
+ Log.d(TAG, "IHostapd died: cookie=" + cookie);
+ if (cookie != waitForDeathCookie) return;
+ waitForDeathLatch.countDown();
+ }, waitForDeathCookie);
+
final String methodStr = "terminate";
if (!checkHostapdAndLogFailure(methodStr)) return;
try {
@@ -497,6 +538,15 @@
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
+
+ // Now wait for death listener callback to confirm that it's dead.
+ try {
+ if (!waitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ Log.w(TAG, "Timed out waiting for confirmation of hostapd death");
+ }
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Failed to wait for hostapd death");
+ }
}
}
diff --git a/service/java/com/android/server/wifi/MacAddressUtil.java b/service/java/com/android/server/wifi/MacAddressUtil.java
new file mode 100644
index 0000000..4739b61
--- /dev/null
+++ b/service/java/com/android/server/wifi/MacAddressUtil.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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 android.net.MacAddress;
+import android.net.wifi.WifiConfiguration;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.ProviderException;
+import java.security.UnrecoverableKeyException;
+import java.util.Arrays;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+
+/**
+ * Contains helper methods to support MAC randomization.
+ */
+public class MacAddressUtil {
+ private static final String TAG = "MacAddressUtil";
+ private static final String MAC_RANDOMIZATION_ALIAS = "MacRandSecret";
+ private static final long MAC_ADDRESS_VALID_LONG_MASK = (1L << 48) - 1;
+ private static final long MAC_ADDRESS_LOCALLY_ASSIGNED_MASK = 1L << 41;
+ private static final long MAC_ADDRESS_MULTICAST_MASK = 1L << 40;
+
+ /**
+ * Computes the persistent randomized MAC of the given configuration using the given
+ * hash function.
+ * @param config the WifiConfiguration to compute MAC address for
+ * @param hashFunction the hash function that will perform the MAC address computation.
+ * @return The persistent randomized MAC address or null if inputs are invalid.
+ */
+ public MacAddress calculatePersistentMacForConfiguration(WifiConfiguration config,
+ Mac hashFunction) {
+ if (config == null || hashFunction == null) {
+ return null;
+ }
+ byte[] hashedBytes;
+ try {
+ hashedBytes = hashFunction.doFinal(config.getSsidAndSecurityTypeString()
+ .getBytes(StandardCharsets.UTF_8));
+ } catch (ProviderException | IllegalStateException e) {
+ Log.e(TAG, "Failure in calculatePersistentMac", e);
+ return null;
+ }
+ ByteBuffer bf = ByteBuffer.wrap(hashedBytes);
+ long longFromSsid = bf.getLong();
+ /**
+ * Masks the generated long so that it represents a valid randomized MAC address.
+ * Specifically, this sets the locally assigned bit to 1, multicast bit to 0
+ */
+ longFromSsid &= MAC_ADDRESS_VALID_LONG_MASK;
+ longFromSsid |= MAC_ADDRESS_LOCALLY_ASSIGNED_MASK;
+ longFromSsid &= ~MAC_ADDRESS_MULTICAST_MASK;
+ bf.clear();
+ bf.putLong(0, longFromSsid);
+
+ // MacAddress.fromBytes requires input of length 6, which is obtained from the
+ // last 6 bytes from the generated long.
+ MacAddress macAddress = MacAddress.fromBytes(Arrays.copyOfRange(bf.array(), 2, 8));
+ return macAddress;
+ }
+
+ /**
+ * Retrieves a Hash function that could be used to calculate the persistent randomized MAC
+ * for a WifiConfiguration.
+ * @param uid the UID of the KeyStore to get the secret of the hash function from.
+ */
+ public Mac obtainMacRandHashFunction(int uid) {
+ try {
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(uid);
+ // tries to retrieve the secret, and generate a new one if it's unavailable.
+ Key key = keyStore.getKey(MAC_RANDOMIZATION_ALIAS, null);
+ if (key == null) {
+ key = generateAndPersistNewMacRandomizationSecret(uid);
+ }
+ if (key == null) {
+ Log.e(TAG, "Failed to generate secret for " + MAC_RANDOMIZATION_ALIAS);
+ return null;
+ }
+ Mac result = Mac.getInstance("HmacSHA256");
+ result.init(key);
+ return result;
+ } catch (KeyStoreException | NoSuchAlgorithmException | InvalidKeyException
+ | UnrecoverableKeyException | NoSuchProviderException e) {
+ Log.e(TAG, "Failure in obtainMacRandHashFunction", e);
+ return null;
+ }
+ }
+
+ /**
+ * Generates and returns a secret key to use for Mac randomization.
+ * Will also persist the generated secret inside KeyStore, accessible in the
+ * future with KeyGenerator#getKey.
+ */
+ private SecretKey generateAndPersistNewMacRandomizationSecret(int uid) {
+ try {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
+ keyGenerator.init(
+ new KeyGenParameterSpec.Builder(MAC_RANDOMIZATION_ALIAS,
+ KeyProperties.PURPOSE_SIGN)
+ .setUid(uid)
+ .build());
+ return keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
+ | NoSuchProviderException | ProviderException e) {
+ Log.e(TAG, "Failure in generateMacRandomizationSecret", e);
+ return null;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java
index 6966471..52e655b 100644
--- a/service/java/com/android/server/wifi/NetworkListStoreData.java
+++ b/service/java/com/android/server/wifi/NetworkListStoreData.java
@@ -16,6 +16,9 @@
package com.android.server.wifi;
+import static com.android.server.wifi.WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION;
+
+import android.annotation.Nullable;
import android.content.Context;
import android.net.IpConfiguration;
import android.net.wifi.WifiConfiguration;
@@ -25,6 +28,7 @@
import android.util.Log;
import android.util.Pair;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil;
@@ -66,19 +70,22 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
- serializeNetworkList(out, mConfigurations);
+ serializeNetworkList(out, mConfigurations, encryptionUtil);
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
return;
}
- mConfigurations = parseNetworkList(in, outerTagDepth);
+ mConfigurations = parseNetworkList(in, outerTagDepth, version, encryptionUtil);
}
@Override
@@ -118,33 +125,38 @@
*
* @param out The output stream to serialize the data to
* @param networkList The network list to serialize
+ * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil}
* @throws XmlPullParserException
* @throws IOException
*/
- private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList)
+ private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (networkList == null) {
return;
}
for (WifiConfiguration network : networkList) {
- serializeNetwork(out, network);
+ serializeNetwork(out, network, encryptionUtil);
}
}
/**
* Serialize a {@link WifiConfiguration} to an output stream in XML format.
- * @param out
- * @param config
+ *
+ * @param out The output stream to serialize the data to
+ * @param config The network config to serialize
+ * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil}
* @throws XmlPullParserException
* @throws IOException
*/
- private void serializeNetwork(XmlSerializer out, WifiConfiguration config)
+ private void serializeNetwork(XmlSerializer out, WifiConfiguration config,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
// Serialize WifiConfiguration.
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
- WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config, encryptionUtil);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
// Serialize network selection status.
@@ -162,7 +174,7 @@
&& config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
XmlUtil.writeNextSectionStart(
out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
- WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig);
+ WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig, encryptionUtil);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
@@ -174,11 +186,15 @@
*
* @param in The input stream to read from
* @param outerTagDepth The XML tag depth of the outer XML block
+ * @param version Version of config store file.
+ * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil}
* @return List of {@link WifiConfiguration}
* @throws XmlPullParserException
* @throws IOException
*/
- private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth)
+ private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
List<WifiConfiguration> networkList = new ArrayList<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK,
@@ -186,7 +202,8 @@
// Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
// fatal and should abort the entire loading process.
try {
- WifiConfiguration config = parseNetwork(in, outerTagDepth + 1);
+ WifiConfiguration config =
+ parseNetwork(in, outerTagDepth + 1, version, encryptionUtil);
networkList.add(config);
} catch (RuntimeException e) {
// Failed to parse this network, skip it.
@@ -201,11 +218,15 @@
*
* @param in The input stream to read from
* @param outerTagDepth The XML tag depth of the outer XML block
+ * @param version Version of config store file.
+ * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil}
* @return {@link WifiConfiguration}
* @throws XmlPullParserException
* @throws IOException
*/
- private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth)
+ private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
Pair<String, WifiConfiguration> parsedConfig = null;
NetworkSelectionStatus status = null;
@@ -220,7 +241,9 @@
throw new XmlPullParserException("Detected duplicate tag for: "
+ XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
}
- parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1,
+ version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ encryptionUtil);
break;
case XML_TAG_SECTION_HEADER_NETWORK_STATUS:
if (status != null) {
@@ -242,7 +265,9 @@
+ XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
enterpriseConfig =
- WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1,
+ version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ encryptionUtil);
break;
default:
throw new XmlPullParserException("Unknown tag under "
diff --git a/service/java/com/android/server/wifi/NetworkRequestStoreData.java b/service/java/com/android/server/wifi/NetworkRequestStoreData.java
index b74e644..7457079 100644
--- a/service/java/com/android/server/wifi/NetworkRequestStoreData.java
+++ b/service/java/com/android/server/wifi/NetworkRequestStoreData.java
@@ -16,10 +16,12 @@
package com.android.server.wifi;
+import android.annotation.Nullable;
import android.net.MacAddress;
import android.util.Log;
import com.android.server.wifi.WifiNetworkFactory.AccessPoint;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
@@ -29,7 +31,7 @@
import java.io.IOException;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -87,13 +89,16 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
serializeApprovedAccessPointsMap(out, mDataSource.toSerialize());
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
@@ -213,7 +218,7 @@
*/
private Set<AccessPoint> parseApprovedAccessPoints(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
- Set<AccessPoint> approvedAccessPoints = new HashSet<>();
+ Set<AccessPoint> approvedAccessPoints = new LinkedHashSet<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
in, XML_TAG_SECTION_HEADER_ACCESS_POINT, outerTagDepth)) {
// Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
diff --git a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java
index 9627a9d..e973bdb 100644
--- a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java
+++ b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java
@@ -16,6 +16,9 @@
package com.android.server.wifi;
+import static com.android.server.wifi.WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION;
+
+import android.annotation.Nullable;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiNetworkSuggestion;
@@ -26,6 +29,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion;
import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
@@ -98,19 +102,23 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
- serializeNetworkSuggestionsMap(out, mDataSource.toSerialize());
+ serializeNetworkSuggestionsMap(out, mDataSource.toSerialize(), encryptionUtil);
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
return;
}
- mDataSource.fromDeserialized(parseNetworkSuggestionsMap(in, outerTagDepth));
+ mDataSource.fromDeserialized(
+ parseNetworkSuggestionsMap(in, outerTagDepth, version, encryptionUtil));
}
@Override
@@ -140,7 +148,8 @@
* @throws IOException
*/
private void serializeNetworkSuggestionsMap(
- XmlSerializer out, final Map<String, PerAppInfo> networkSuggestionsMap)
+ XmlSerializer out, final Map<String, PerAppInfo> networkSuggestionsMap,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (networkSuggestionsMap == null) {
return;
@@ -155,7 +164,7 @@
XmlUtil.writeNextValue(out, XML_TAG_SUGGESTOR_PACKAGE_NAME, packageName);
XmlUtil.writeNextValue(out, XML_TAG_SUGGESTOR_HAS_USER_APPROVED, hasUserApproved);
XmlUtil.writeNextValue(out, XML_TAG_SUGGESTOR_MAX_SIZE, maxSize);
- serializeExtNetworkSuggestions(out, networkSuggestions);
+ serializeExtNetworkSuggestions(out, networkSuggestions, encryptionUtil);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_SUGGESTION_PER_APP);
}
}
@@ -167,10 +176,11 @@
* @throws IOException
*/
private void serializeExtNetworkSuggestions(
- XmlSerializer out, final Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions)
+ XmlSerializer out, final Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
for (ExtendedWifiNetworkSuggestion extNetworkSuggestion : extNetworkSuggestions) {
- serializeNetworkSuggestion(out, extNetworkSuggestion.wns);
+ serializeNetworkSuggestion(out, extNetworkSuggestion.wns, encryptionUtil);
}
}
@@ -181,13 +191,15 @@
* @throws IOException
*/
private void serializeNetworkSuggestion(XmlSerializer out,
- final WifiNetworkSuggestion suggestion)
+ final WifiNetworkSuggestion suggestion,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_SUGGESTION);
// Serialize WifiConfiguration.
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
- WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, suggestion.wifiConfiguration);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(
+ out, suggestion.wifiConfiguration, encryptionUtil);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
// Serialize enterprise configuration for enterprise networks.
if (suggestion.wifiConfiguration.enterpriseConfig != null
@@ -196,7 +208,7 @@
XmlUtil.writeNextSectionStart(
out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
XmlUtil.WifiEnterpriseConfigXmlUtil.writeToXml(
- out, suggestion.wifiConfiguration.enterpriseConfig);
+ out, suggestion.wifiConfiguration.enterpriseConfig, encryptionUtil);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
@@ -218,7 +230,9 @@
* @throws XmlPullParserException
* @throws IOException
*/
- private Map<String, PerAppInfo> parseNetworkSuggestionsMap(XmlPullParser in, int outerTagDepth)
+ private Map<String, PerAppInfo> parseNetworkSuggestionsMap(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
Map<String, PerAppInfo> networkSuggestionsMap = new HashMap<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
@@ -233,7 +247,8 @@
int maxSize = (int) XmlUtil.readNextValueWithName(in, XML_TAG_SUGGESTOR_MAX_SIZE);
PerAppInfo perAppInfo = new PerAppInfo(packageName);
Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions =
- parseExtNetworkSuggestions(in, outerTagDepth + 1, perAppInfo);
+ parseExtNetworkSuggestions(
+ in, outerTagDepth + 1, version, encryptionUtil, perAppInfo);
perAppInfo.hasUserApproved = hasUserApproved;
perAppInfo.maxSize = maxSize;
perAppInfo.extNetworkSuggestions.addAll(extNetworkSuggestions);
@@ -253,7 +268,8 @@
* @throws IOException
*/
private Set<ExtendedWifiNetworkSuggestion> parseExtNetworkSuggestions(
- XmlPullParser in, int outerTagDepth, PerAppInfo perAppInfo)
+ XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil, PerAppInfo perAppInfo)
throws XmlPullParserException, IOException {
Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = new HashSet<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
@@ -262,7 +278,7 @@
// fatal and should abort the entire loading process.
try {
WifiNetworkSuggestion networkSuggestion =
- parseNetworkSuggestion(in, outerTagDepth + 1);
+ parseNetworkSuggestion(in, outerTagDepth + 1, version, encryptionUtil);
extNetworkSuggestions.add(ExtendedWifiNetworkSuggestion.fromWns(
networkSuggestion, perAppInfo));
} catch (RuntimeException e) {
@@ -279,7 +295,9 @@
* @throws XmlPullParserException
* @throws IOException
*/
- private WifiNetworkSuggestion parseNetworkSuggestion(XmlPullParser in, int outerTagDepth)
+ private WifiNetworkSuggestion parseNetworkSuggestion(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
Pair<String, WifiConfiguration> parsedConfig = null;
WifiEnterpriseConfig enterpriseConfig = null;
@@ -324,7 +342,9 @@
+ XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
}
parsedConfig = WifiConfigurationXmlUtil.parseFromXml(
- in, outerTagDepth + 1);
+ in, outerTagDepth + 1,
+ version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ encryptionUtil);
break;
case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION:
if (enterpriseConfig != null) {
@@ -332,7 +352,9 @@
+ XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
enterpriseConfig = XmlUtil.WifiEnterpriseConfigXmlUtil.parseFromXml(
- in, outerTagDepth + 1);
+ in, outerTagDepth + 1,
+ version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ encryptionUtil);
break;
default:
throw new XmlPullParserException("Unknown tag under "
diff --git a/service/java/com/android/server/wifi/RandomizedMacStoreData.java b/service/java/com/android/server/wifi/RandomizedMacStoreData.java
index 1e4d972..ecbd717 100644
--- a/service/java/com/android/server/wifi/RandomizedMacStoreData.java
+++ b/service/java/com/android/server/wifi/RandomizedMacStoreData.java
@@ -16,6 +16,9 @@
package com.android.server.wifi;
+import android.annotation.Nullable;
+
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -40,7 +43,8 @@
RandomizedMacStoreData() {}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (mMacMapping != null) {
XmlUtil.writeNextValue(out, XML_TAG_MAC_MAP, mMacMapping);
@@ -48,7 +52,9 @@
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java
index efbb7b6..a467844 100644
--- a/service/java/com/android/server/wifi/ScanRequestProxy.java
+++ b/service/java/com/android/server/wifi/ScanRequestProxy.java
@@ -484,11 +484,14 @@
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
if (mScanningForHiddenNetworksEnabled) {
- // retrieve the list of hidden network SSIDs to scan for, if enabled.
+ // retrieve the list of hidden network SSIDs from saved network to scan for, if enabled.
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
- mWifiConfigManager.retrieveHiddenNetworkList();
+ new ArrayList<>(mWifiConfigManager.retrieveHiddenNetworkList());
+ // retrieve the list of hidden network SSIDs from Network suggestion to scan for.
+ hiddenNetworkList.addAll(
+ mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
settings.hiddenNetworks = hiddenNetworkList.toArray(
- new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
+ new WifiScanner.ScanSettings.HiddenNetwork[0]);
}
mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
return true;
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index b92dab7..79bc46f 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -95,6 +95,8 @@
private long mStartTimestamp = -1;
+ private BaseWifiDiagnostics mWifiDiagnostics;
+
/**
* Listener for soft AP events.
*/
@@ -127,7 +129,8 @@
@NonNull WifiApConfigStore wifiApConfigStore,
@NonNull SoftApModeConfiguration apConfig,
@NonNull WifiMetrics wifiMetrics,
- @NonNull SarManager sarManager) {
+ @NonNull SarManager sarManager,
+ @NonNull BaseWifiDiagnostics wifiDiagnostics) {
mContext = context;
mFrameworkFacade = framework;
mWifiNative = wifiNative;
@@ -143,6 +146,7 @@
}
mWifiMetrics = wifiMetrics;
mSarManager = sarManager;
+ mWifiDiagnostics = wifiDiagnostics;
mStateMachine = new SoftApStateMachine(looper);
}
@@ -287,6 +291,7 @@
Log.e(TAG, "Soft AP start failed");
return ERROR_GENERIC;
}
+ mWifiDiagnostics.startLogging(mApInterfaceName);
mStartTimestamp = SystemClock.elapsedRealtime();
Log.d(TAG, "Soft AP is started");
@@ -297,6 +302,7 @@
* Teardown soft AP and teardown the interface.
*/
private void stopSoftAp() {
+ mWifiDiagnostics.stopLogging(mApInterfaceName);
mWifiNative.teardownInterface(mApInterfaceName);
Log.d(TAG, "Soft AP is stopped");
}
diff --git a/service/java/com/android/server/wifi/SsidSetStoreData.java b/service/java/com/android/server/wifi/SsidSetStoreData.java
index 7d1b993..36b547c 100644
--- a/service/java/com/android/server/wifi/SsidSetStoreData.java
+++ b/service/java/com/android/server/wifi/SsidSetStoreData.java
@@ -16,8 +16,10 @@
package com.android.server.wifi;
+import android.annotation.Nullable;
import android.text.TextUtils;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -74,7 +76,8 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
Set<String> ssidSet = mDataSource.getSsids();
if (ssidSet != null && !ssidSet.isEmpty()) {
@@ -83,7 +86,9 @@
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
index bed66d9..8db7c90 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -86,6 +86,9 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -108,6 +111,8 @@
public static final String INIT_STOP_PROPERTY = "ctl.stop";
@VisibleForTesting
public static final String INIT_SERVICE_NAME = "wpa_supplicant";
+ @VisibleForTesting
+ public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L;
/**
* Regex pattern for extracting the wps device type bytes.
* Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
@@ -262,11 +267,12 @@
}
}
- private boolean linkToSupplicantDeath() {
+ private boolean linkToSupplicantDeath(
+ HwRemoteBinder.DeathRecipient deathRecipient, long cookie) {
synchronized (mLock) {
if (mISupplicant == null) return false;
try {
- if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, ++mDeathRecipientCookie)) {
+ if (!mISupplicant.linkToDeath(deathRecipient, cookie)) {
Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
supplicantServiceDiedHandler(mDeathRecipientCookie);
return false;
@@ -294,7 +300,7 @@
Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
return false;
}
- if (!linkToSupplicantDeath()) {
+ if (!linkToSupplicantDeath(mSupplicantDeathRecipient, ++mDeathRecipientCookie)) {
return false;
}
}
@@ -645,10 +651,19 @@
}
/**
- * Terminate the supplicant daemon.
+ * Terminate the supplicant daemon & wait for it's death.
*/
public void terminate() {
synchronized (mLock) {
+ // Register for a new death listener to block until supplicant is dead.
+ final long waitForDeathCookie = new Random().nextLong();
+ final CountDownLatch waitForDeathLatch = new CountDownLatch(1);
+ linkToSupplicantDeath((cookie) -> {
+ Log.d(TAG, "ISupplicant died: cookie=" + cookie);
+ if (cookie != waitForDeathCookie) return;
+ waitForDeathLatch.countDown();
+ }, waitForDeathCookie);
+
if (isV1_1()) {
Log.i(TAG, "Terminating supplicant using HIDL");
terminate_V1_1();
@@ -656,6 +671,15 @@
Log.i(TAG, "Terminating supplicant using init");
mPropertyService.set(INIT_STOP_PROPERTY, INIT_SERVICE_NAME);
}
+
+ // Now wait for death listener callback to confirm that it's dead.
+ try {
+ if (!waitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ Log.w(TAG, "Timed out waiting for confirmation of supplicant death");
+ }
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Failed to wait for supplicant death");
+ }
}
}
@@ -3050,10 +3074,10 @@
* This is a v1.2+ HAL feature.
* On error, or if these features are not supported, 0 is returned.
*/
- public int getAdvancedKeyMgmtCapabilities(@NonNull String ifaceName) {
+ public long getAdvancedKeyMgmtCapabilities(@NonNull String ifaceName) {
final String methodStr = "getAdvancedKeyMgmtCapabilities";
- int advancedCapabilities = 0;
+ long advancedCapabilities = 0;
int keyMgmtCapabilities = getKeyMgmtCapabilities(ifaceName);
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork
diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
index 9255fc2..dd56b5f 100644
--- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
@@ -339,19 +339,11 @@
Log.e(TAG, config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
return false;
}
- // The logic below is skipping WPA2-Enterprise explicit setting of PMF to disabled
- // in order to allow connection to networks with PMF required. Skipping means that
- // wpa_supplicant will use the global setting (optional/capable).
- // TODO(b/130755779): A permanent fix should convert requirePMF to a tri-state variablbe
- boolean wpa2EnterpriseSkipPmf = !config.requirePMF
- && (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
- || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+
/** RequirePMF */
- if (!wpa2EnterpriseSkipPmf) {
- if (!setRequirePmf(config.requirePMF)) {
- Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
- return false;
- }
+ if (!setRequirePmf(config.requirePMF)) {
+ Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
+ return false;
}
/** Key Management Scheme */
if (config.allowedKeyManagement.cardinality() != 0) {
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
index d191ee3..847d8fb 100644
--- a/service/java/com/android/server/wifi/WakeupConfigStoreData.java
+++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
@@ -16,10 +16,12 @@
package com.android.server.wifi;
+import android.annotation.Nullable;
import android.util.ArraySet;
import android.util.Log;
import com.android.server.wifi.WifiConfigStore.StoreData;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -94,7 +96,8 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
writeFeatureState(out);
@@ -141,7 +144,9 @@
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (!mHasBeenRead) {
Log.d(TAG, "WifiWake user data has been read");
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index b22b7b0..8dcd1c1 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -274,6 +274,8 @@
private final WifiPermissionsUtil mWifiPermissionsUtil;
private final WifiPermissionsWrapper mWifiPermissionsWrapper;
private final WifiInjector mWifiInjector;
+ private final MacAddressUtil mMacAddressUtil;
+ private boolean mConnectedMacRandomzationSupported;
/**
* Local log used for debugging any WifiConfigManager issues.
@@ -316,6 +318,7 @@
private final int mMaxNumActiveChannelsForPartialScans;
private final FrameworkFacade mFrameworkFacade;
+ private final DeviceConfigFacade mDeviceConfigFacade;
/**
* Verbose logging flag. Toggled by developer options.
@@ -373,6 +376,7 @@
private boolean mPnoFrequencyCullingEnabled = false;
private boolean mPnoRecencySortingEnabled = false;
+ private Set<String> mRandomizationFlakySsidHotlist;
@@ -390,7 +394,8 @@
NetworkListUserStoreData networkListUserStoreData,
DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData,
RandomizedMacStoreData randomizedMacStoreData,
- FrameworkFacade frameworkFacade, Looper looper) {
+ FrameworkFacade frameworkFacade, Looper looper,
+ DeviceConfigFacade deviceConfigFacade) {
mContext = context;
mClock = clock;
mUserManager = userManager;
@@ -440,12 +445,23 @@
}
});
updatePnoRecencySortingSetting();
+ mConnectedMacRandomzationSupported = mContext.getResources()
+ .getBoolean(R.bool.config_wifi_connected_mac_randomization_supported);
+ mDeviceConfigFacade = deviceConfigFacade;
+ mDeviceConfigFacade.addOnPropertiesChangedListener(
+ command -> new Handler(looper).post(command),
+ properties -> {
+ mRandomizationFlakySsidHotlist =
+ mDeviceConfigFacade.getRandomizationFlakySsidHotlist();
+ });
+ mRandomizationFlakySsidHotlist = mDeviceConfigFacade.getRandomizationFlakySsidHotlist();
try {
mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME,
PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to resolve SystemUI's UID.");
}
+ mMacAddressUtil = mWifiInjector.getMacAddressUtil();
}
/**
@@ -497,11 +513,11 @@
mRandomizedMacAddressMapping.remove(config.getSsidAndSecurityTypeString());
}
}
- MacAddress result = WifiConfigurationUtil.calculatePersistentMacForConfiguration(config,
- WifiConfigurationUtil.obtainMacRandHashFunction(Process.WIFI_UID));
+ MacAddress result = mMacAddressUtil.calculatePersistentMacForConfiguration(
+ config, mMacAddressUtil.obtainMacRandHashFunction(Process.WIFI_UID));
if (result == null) {
- result = WifiConfigurationUtil.calculatePersistentMacForConfiguration(config,
- WifiConfigurationUtil.obtainMacRandHashFunction(Process.WIFI_UID));
+ result = mMacAddressUtil.calculatePersistentMacForConfiguration(
+ config, mMacAddressUtil.obtainMacRandHashFunction(Process.WIFI_UID));
}
if (result == null) {
Log.wtf(TAG, "Failed to generate MAC address from KeyStore even after retrying. "
@@ -607,6 +623,9 @@
&& targetUid != configuration.creatorUid) {
maskRandomizedMacAddressInWifiConfiguration(network);
}
+ if (!mConnectedMacRandomzationSupported) {
+ network.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ }
return network;
}
@@ -1528,10 +1547,29 @@
}
/**
+ * Check whether a network belong to a known list of networks that may not support randomized
+ * MAC.
+ * @param networkId
+ * @return true if the network is in the hotlist and MAC randomization is enabled.
+ */
+ public boolean isInFlakyRandomizationSsidHotlist(int networkId) {
+ WifiConfiguration config = getConfiguredNetwork(networkId);
+ return config != null
+ && config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT
+ && mRandomizationFlakySsidHotlist.contains(config.SSID);
+ }
+
+ /**
* Helper method to mark a network enabled for network selection.
*/
private void setNetworkSelectionEnabled(WifiConfiguration config) {
NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ if (status.getNetworkSelectionStatus()
+ != NetworkSelectionStatus.NETWORK_SELECTION_ENABLED) {
+ localLog("setNetworkSelectionEnabled: configKey=" + config.configKey()
+ + " old networkStatus=" + status.getNetworkStatusString()
+ + " disableReason=" + status.getNetworkDisableReasonString());
+ }
status.setNetworkSelectionStatus(
NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
status.setDisableTime(
@@ -2728,7 +2766,7 @@
}
/**
- * Retrieves a list of all the saved hidden networks for scans.
+ * Retrieves a list of all the saved hidden networks for scans
*
* Hidden network list sent to the firmware has limited size. If there are a lot of saved
* networks, this list will be truncated and we might end up not sending the networks
@@ -2741,19 +2779,12 @@
public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
- // Remove any permanently disabled networks or non hidden networks.
- Iterator<WifiConfiguration> iter = networks.iterator();
- while (iter.hasNext()) {
- WifiConfiguration config = iter.next();
- if (!config.hiddenSSID) {
- iter.remove();
- }
- }
- Collections.sort(networks, sScanListComparator);
+ // Remove any non hidden networks.
+ networks.removeIf(config -> !config.hiddenSSID);
+ networks.sort(sScanListComparator);
// The most frequently connected network has the highest priority now.
for (WifiConfiguration config : networks) {
- hiddenList.add(
- new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
+ hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
}
return hiddenList;
}
@@ -3141,7 +3172,8 @@
if (mDeferredUserUnlockRead) {
Log.i(TAG, "Handling user unlock before loading from store.");
List<WifiConfigStore.StoreFile> userStoreFiles =
- WifiConfigStore.createUserFiles(mCurrentUserId);
+ WifiConfigStore.createUserFiles(
+ mCurrentUserId, mFrameworkFacade.isNiapModeOn(mContext));
if (userStoreFiles == null) {
Log.wtf(TAG, "Failed to create user store files");
return false;
@@ -3151,7 +3183,7 @@
}
try {
mWifiConfigStore.read();
- } catch (IOException e) {
+ } catch (IOException | IllegalStateException e) {
Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
return false;
} catch (XmlPullParserException e) {
@@ -3180,18 +3212,19 @@
private boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
try {
List<WifiConfigStore.StoreFile> userStoreFiles =
- WifiConfigStore.createUserFiles(userId);
+ WifiConfigStore.createUserFiles(
+ userId, mFrameworkFacade.isNiapModeOn(mContext));
if (userStoreFiles == null) {
Log.e(TAG, "Failed to create user store files");
return false;
}
mWifiConfigStore.switchUserStoresAndRead(userStoreFiles);
- } catch (IOException e) {
+ } catch (IOException | IllegalStateException e) {
Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
return false;
} catch (XmlPullParserException e) {
- Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
- "lost!", e);
+ Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are "
+ + "lost!", e);
return false;
}
loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations(),
@@ -3261,7 +3294,7 @@
try {
mWifiConfigStore.write(forceWrite);
- } catch (IOException e) {
+ } catch (IOException | IllegalStateException e) {
Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
return false;
} catch (XmlPullParserException e) {
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index a618eb5..42d9f82 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -35,7 +35,8 @@
import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
-import com.android.server.wifi.util.DataIntegrityChecker;
+import com.android.server.wifi.util.EncryptedData;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -53,7 +54,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
-import java.security.DigestException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -61,6 +61,7 @@
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* This class provides a mechanism to save data to persistent store files {@link StoreFile}.
@@ -99,15 +100,37 @@
private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData";
private static final String XML_TAG_VERSION = "Version";
+ private static final String XML_TAG_HEADER_INTEGRITY = "Integrity";
/**
* Current config store data version. This will be incremented for any additions.
*/
- private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 1;
+ private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 3;
/** This list of older versions will be used to restore data from older config store. */
/**
* First version of the config store data format.
*/
- private static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1;
+ public static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1;
+ /**
+ * Second version of the config store data format, introduced:
+ * - Integrity info.
+ */
+ public static final int INTEGRITY_CONFIG_STORE_DATA_VERSION = 2;
+ /**
+ * Third version of the config store data format,
+ * introduced:
+ * - Encryption of credentials
+ * removed:
+ * - Integrity info.
+ */
+ public static final int ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION = 3;
+
+ @IntDef(suffix = { "_VERSION" }, value = {
+ INITIAL_CONFIG_STORE_DATA_VERSION,
+ INTEGRITY_CONFIG_STORE_DATA_VERSION,
+ ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Version { }
/**
* Alarm tag to use for starting alarms for buffering file writes.
@@ -148,7 +171,6 @@
put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL);
put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS);
}};
-
/**
* Handler instance to post alarm timeouts to
*/
@@ -266,9 +288,11 @@
* @param storeBaseDir Base directory under which the store file is to be stored. The store file
* will be at <storeBaseDir>/wifi/WifiConfigStore.xml.
* @param fileId Identifier for the file. See {@link StoreFileId}.
+ * @param shouldEncryptCredentials Whether to encrypt credentials or not.
* @return new instance of the store file or null if the directory cannot be created.
*/
- private static @Nullable StoreFile createFile(File storeBaseDir, @StoreFileId int fileId) {
+ private static @Nullable StoreFile createFile(File storeBaseDir, @StoreFileId int fileId,
+ boolean shouldEncryptCredentials) {
File storeDir = new File(storeBaseDir, STORE_DIRECTORY_NAME);
if (!storeDir.exists()) {
if (!storeDir.mkdir()) {
@@ -276,16 +300,23 @@
return null;
}
}
- return new StoreFile(new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId)), fileId);
+ File file = new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId));
+ WifiConfigStoreEncryptionUtil encryptionUtil = null;
+ if (shouldEncryptCredentials) {
+ encryptionUtil = new WifiConfigStoreEncryptionUtil(file.getName());
+ }
+ return new StoreFile(file, fileId, encryptionUtil);
}
/**
* Create a new instance of the shared store file.
*
+ * @param shouldEncryptCredentials Whether to encrypt credentials or not.
* @return new instance of the store file or null if the directory cannot be created.
*/
- public static @Nullable StoreFile createSharedFile() {
- return createFile(Environment.getDataMiscDirectory(), STORE_FILE_SHARED_GENERAL);
+ public static @Nullable StoreFile createSharedFile(boolean shouldEncryptCredentials) {
+ return createFile(Environment.getDataMiscDirectory(), STORE_FILE_SHARED_GENERAL,
+ shouldEncryptCredentials);
}
/**
@@ -293,14 +324,18 @@
* The user store file is inside the user's encrypted data directory.
*
* @param userId userId corresponding to the currently logged-in user.
+ * @param shouldEncryptCredentials Whether to encrypt credentials or not.
* @return List of new instances of the store files created or null if the directory cannot be
* created.
*/
- public static @Nullable List<StoreFile> createUserFiles(int userId) {
+ public static @Nullable List<StoreFile> createUserFiles(int userId,
+ boolean shouldEncryptCredentials) {
List<StoreFile> storeFiles = new ArrayList<>();
for (int fileId : Arrays.asList(
STORE_FILE_USER_GENERAL, STORE_FILE_USER_NETWORK_SUGGESTIONS)) {
- StoreFile storeFile = createFile(Environment.getDataMiscCeDirectory(userId), fileId);
+ StoreFile storeFile =
+ createFile(Environment.getDataMiscCeDirectory(userId), fileId,
+ shouldEncryptCredentials);
if (storeFile == null) {
return null;
}
@@ -395,6 +430,9 @@
* Serialize all the data from all the {@link StoreData} clients registered for the provided
* {@link StoreFile}.
*
+ * This method also computes the integrity of the data being written and serializes the computed
+ * {@link EncryptedData} to the output.
+ *
* @param storeFile StoreFile that we want to write to.
* @return byte[] of serialized bytes
* @throws XmlPullParserException
@@ -408,16 +446,17 @@
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ // First XML header.
XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
+ // Next version.
XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION);
for (StoreData storeData : storeDataList) {
String tag = storeData.getName();
XmlUtil.writeNextSectionStart(out, tag);
- storeData.serializeData(out);
+ storeData.serializeData(out, storeFile.getEncryptionUtil());
XmlUtil.writeNextSectionEnd(out, tag);
}
XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
-
return outputStream.toByteArray();
}
@@ -539,16 +578,21 @@
}
// Inform all the provided store data clients that there is nothing in the store for them.
- private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet)
+ private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet,
+ @Version int version, @NonNull WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
for (StoreData storeData : storeDataSet) {
- storeData.deserializeData(null, 0);
+ storeData.deserializeData(null, 0, version, encryptionUtil);
}
}
/**
* Deserialize data from a {@link StoreFile} for all {@link StoreData} instances registered.
*
+ * This method also computes the integrity of the incoming |dataBytes| and compare with
+ * {@link EncryptedData} parsed from |dataBytes|. If the integrity check fails, the data
+ * is discarded.
+ *
* @param dataBytes The data to parse
* @param storeFile StoreFile that we read from. Will be used to retrieve the list of clients
* who have data to deserialize from this file.
@@ -560,7 +604,8 @@
throws XmlPullParserException, IOException {
List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile);
if (dataBytes == null) {
- indicateNoDataForStoreDatas(storeDataList);
+ indicateNoDataForStoreDatas(storeDataList, -1 /* unknown */,
+ storeFile.getEncryptionUtil());
return;
}
final XmlPullParser in = Xml.newPullParser();
@@ -569,7 +614,13 @@
// Start parsing the XML stream.
int rootTagDepth = in.getDepth() + 1;
- parseDocumentStartAndVersionFromXml(in);
+ XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
+
+ @Version int version = parseVersionFromXml(in);
+ // Version 2 contains the now unused integrity data, parse & then discard the information.
+ if (version == INTEGRITY_CONFIG_STORE_DATA_VERSION) {
+ parseAndDiscardIntegrityDataFromXml(in, rootTagDepth);
+ }
String[] headerName = new String[1];
Set<StoreData> storeDatasInvoked = new HashSet<>();
@@ -584,26 +635,26 @@
throw new XmlPullParserException("Unknown store data: " + headerName[0]
+ ". List of store data: " + storeDataList);
}
- storeData.deserializeData(in, rootTagDepth + 1);
+ storeData.deserializeData(in, rootTagDepth + 1, version,
+ storeFile.getEncryptionUtil());
storeDatasInvoked.add(storeData);
}
// Inform all the other registered store data clients that there is nothing in the store
// for them.
Set<StoreData> storeDatasNotInvoked = new HashSet<>(storeDataList);
storeDatasNotInvoked.removeAll(storeDatasInvoked);
- indicateNoDataForStoreDatas(storeDatasNotInvoked);
+ indicateNoDataForStoreDatas(storeDatasNotInvoked, version, storeFile.getEncryptionUtil());
}
/**
- * Parse the document start and version from the XML stream.
+ * Parse the version from the XML stream.
* This is used for both the shared and user config store data.
*
* @param in XmlPullParser instance pointing to the XML stream.
* @return version number retrieved from the Xml stream.
*/
- private static int parseDocumentStartAndVersionFromXml(XmlPullParser in)
+ private static @Version int parseVersionFromXml(XmlPullParser in)
throws XmlPullParserException, IOException {
- XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION);
if (version < INITIAL_CONFIG_STORE_DATA_VERSION
|| version > CURRENT_CONFIG_STORE_DATA_VERSION) {
@@ -613,10 +664,29 @@
}
/**
+ * Parse the integrity data structure from the XML stream and discard it.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth Outer tag depth.
+ */
+ private static void parseAndDiscardIntegrityDataFromXml(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ XmlUtil.gotoNextSectionWithName(in, XML_TAG_HEADER_INTEGRITY, outerTagDepth);
+ XmlUtil.EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ }
+
+ /**
* Dump the local log buffer and other internal state of WifiConfigManager.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Dump of WifiConfigStore");
+ pw.println("WifiConfigStore - Store File Begin ----");
+ Stream.of(Arrays.asList(mSharedStore), mUserStores)
+ .flatMap(List::stream)
+ .forEach((storeFile) -> {
+ pw.print("Name: " + storeFile.mFileName);
+ pw.println(", Credentials encrypted: " + storeFile.getEncryptionUtil() != null);
+ });
pw.println("WifiConfigStore - Store Data Begin ----");
for (StoreData storeData : mStoreDataList) {
pw.print("StoreData =>");
@@ -653,21 +723,22 @@
/**
* Store the file name for setting the file permissions/logging purposes.
*/
- private String mFileName;
- /**
- * The integrity file storing integrity checking data for the store file.
- */
- private DataIntegrityChecker mDataIntegrityChecker;
+ private final String mFileName;
/**
* {@link StoreFileId} Type of store file.
*/
- private @StoreFileId int mFileId;
+ private final @StoreFileId int mFileId;
+ /**
+ * Integrity checking for the store file.
+ */
+ private final WifiConfigStoreEncryptionUtil mEncryptionUtil;
- public StoreFile(File file, @StoreFileId int fileId) {
+ public StoreFile(File file, @StoreFileId int fileId,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) {
mAtomicFile = new AtomicFile(file);
- mFileName = mAtomicFile.getBaseFile().getAbsolutePath();
- mDataIntegrityChecker = new DataIntegrityChecker(mFileName);
+ mFileName = file.getAbsolutePath();
mFileId = fileId;
+ mEncryptionUtil = encryptionUtil;
}
/**
@@ -680,6 +751,13 @@
}
/**
+ * @return Returns the encryption util used for this store file.
+ */
+ public @Nullable WifiConfigStoreEncryptionUtil getEncryptionUtil() {
+ return mEncryptionUtil;
+ }
+
+ /**
* Read the entire raw data from the store file and return in a byte array.
*
* @return raw data read from the file or null if the file is not found or the data has
@@ -691,20 +769,8 @@
byte[] bytes = null;
try {
bytes = mAtomicFile.readFully();
- // Check that the file has not been altered since last writeBufferedRawData()
- if (!mDataIntegrityChecker.isOk(bytes)) {
- Log.wtf(TAG, "Data integrity problem with file: " + mFileName);
- return null;
- }
} catch (FileNotFoundException e) {
return null;
- } catch (DigestException e) {
- // When integrity checking is introduced. The existing data will have no related
- // integrity file for validation. Thus, we will assume the existing data is correct
- // and immediately create the integrity file.
- Log.i(TAG, "isOK() had no integrity data to check; thus vacuously "
- + "true. Running update now.");
- mDataIntegrityChecker.update(bytes);
}
return bytes;
}
@@ -741,8 +807,6 @@
}
throw e;
}
- // There was a legitimate change and update the integrity checker.
- mDataIntegrityChecker.update(mWriteData);
// Reset the pending write data after write.
mWriteData = null;
}
@@ -765,8 +829,10 @@
* Serialize a XML data block to the output stream.
*
* @param out The output stream to serialize the data to
+ * @param encryptionUtil Utility to help encrypt any credential data.
*/
- void serializeData(XmlSerializer out)
+ void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException;
/**
@@ -775,10 +841,14 @@
* @param in The input stream to read the data from. This could be null if there is
* nothing in the store.
* @param outerTagDepth The depth of the outer tag in the XML document
+ * @param version Version of config store file.
+ * @param encryptionUtil Utility to help decrypt any credential data.
+ *
* Note: This will be invoked every time a store file is read, even if there is nothing
* in the store for them.
*/
- void deserializeData(@Nullable XmlPullParser in, int outerTagDepth)
+ void deserializeData(@Nullable XmlPullParser in, int outerTagDepth, @Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException;
/**
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index 69a655b..cb83594 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -710,12 +710,14 @@
if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
// PMF mandatory for OWE networks
if (!config.requirePMF) {
+ Log.e(TAG, "PMF must be enabled for OWE networks");
return false;
}
}
if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
// PMF mandatory for WPA3-Personal networks
if (!config.requirePMF) {
+ Log.e(TAG, "PMF must be enabled for SAE networks");
return false;
}
if (!validatePassword(config.preSharedKey, isAdd, true)) {
@@ -725,6 +727,7 @@
if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) {
// PMF mandatory for WPA3-Enterprise networks
if (!config.requirePMF) {
+ Log.e(TAG, "PMF must be enabled for Suite-B 192-bit networks");
return false;
}
}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 7411422..2e4b5c8 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -916,11 +916,14 @@
settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
| WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
settings.numBssidsPerScan = 0;
-
+ // retrieve the list of hidden network SSIDs from saved network to scan for
List<ScanSettings.HiddenNetwork> hiddenNetworkList =
- mConfigManager.retrieveHiddenNetworkList();
+ new ArrayList<>(mConfigManager.retrieveHiddenNetworkList());
+ // retrieve the list of hidden network SSIDs from Network suggestion to scan for
+ hiddenNetworkList.addAll(
+ mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
settings.hiddenNetworks =
- hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
+ hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[0]);
SingleScanListener singleScanListener =
new SingleScanListener(isFullBandScan);
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index a669508..f9d147c 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -48,6 +48,7 @@
private String mTelephonyCountryTimestamp = null;
private String mDriverCountryTimestamp = null;
private String mReadyTimestamp = null;
+ private boolean mForceCountryCode = false;
public WifiCountryCode(
WifiNative wifiNative,
@@ -83,18 +84,6 @@
}
/**
- * This is called when airplane mode is enabled.
- * In this case we should invalidate all other country code except the
- * phone default one.
- */
- public synchronized void airplaneModeEnabled() {
- Log.d(TAG, "Airplane Mode Enabled");
- // Airplane mode is enabled, we need to reset the country code to phone default.
- // Country code will be set upon when wpa_supplicant starts next time.
- mTelephonyCountryCode = null;
- }
-
- /**
* Change the state to indicates if wpa_supplicant is ready to handle country code changing
* request or not.
* We call native code to request country code changes only when wpa_supplicant is
@@ -111,6 +100,36 @@
}
/**
+ * Enable force-country-code mode
+ * @param countryCode The forced two-letter country code
+ */
+ synchronized void enableForceCountryCode(String countryCode) {
+ if (TextUtils.isEmpty(countryCode)) {
+ Log.d(TAG, "Fail to force country code because the received country code is empty");
+ return;
+ }
+ mForceCountryCode = true;
+ mTelephonyCountryCode = countryCode.toUpperCase(Locale.US);
+ // If wpa_supplicant is ready we set the country code now, otherwise it will be
+ // set once wpa_supplicant is ready.
+ if (mReady) {
+ updateCountryCode();
+ } else {
+ Log.d(TAG, "skip update supplicant not ready yet");
+ }
+ }
+
+ /**
+ * Disable force-country-code mode
+ */
+ synchronized void disableForceCountryCode() {
+ mForceCountryCode = false;
+ // Set mTelephonyCountryCode to null so that default country code is used until
+ // next call of setCountryCode().
+ mTelephonyCountryCode = null;
+ }
+
+ /**
* Handle country code change request.
* @param countryCode The country code intended to set.
* This is supposed to be from Telephony service.
@@ -118,6 +137,10 @@
* @return Returns true if the country code passed in is acceptable.
*/
public synchronized boolean setCountryCode(String countryCode) {
+ if (mForceCountryCode) {
+ Log.d(TAG, "Country code can't be set because it is the force-country-code mode");
+ return false;
+ }
Log.d(TAG, "Receive set country code request: " + countryCode);
mTelephonyCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java
index 1054c2e..6eb3b41 100644
--- a/service/java/com/android/server/wifi/WifiDataStall.java
+++ b/service/java/com/android/server/wifi/WifiDataStall.java
@@ -17,6 +17,9 @@
package com.android.server.wifi;
import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent;
@@ -33,19 +36,50 @@
public static final int MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT = 50;
// Maximum time gap between two WifiLinkLayerStats to trigger a data stall
public static final long MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute
+ // Maximum time that a data stall start time stays valid.
+ public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes
+ // Default Tx packet error rate when there is no Tx attempt
+ public static final int DEFAULT_TX_PACKET_ERROR_RATE = 20;
+ // Default CCA level when CCA stats are not available
+ public static final int DEFAULT_CCA_LEVEL = 0;
private final Context mContext;
+ private final DeviceConfigFacade mDeviceConfigFacade;
private final FrameworkFacade mFacade;
private final WifiMetrics mWifiMetrics;
+ private Handler mHandler;
private int mMinTxBad;
private int mMinTxSuccessWithoutRx;
+ private int mDataStallDurationMs;
+ private int mDataStallTxTputThrMbps;
+ private int mDataStallRxTputThrMbps;
+ private int mDataStallTxPerThr;
+ private int mDataStallCcaLevelThr;
+ private int mLastFrequency = -1;
+ private String mLastBssid;
+ private long mLastTotalRadioOnFreqTimeMs = -1;
+ private long mLastTotalCcaBusyFreqTimeMs = -1;
+ private long mDataStallStartTimeMs = -1;
+ private Clock mClock;
+ private boolean mDataStallTx = false;
+ private boolean mDataStallRx = false;
- public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics) {
+ public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics,
+ DeviceConfigFacade deviceConfigFacade, Looper clientModeImplLooper, Clock clock) {
mContext = context;
+ mDeviceConfigFacade = deviceConfigFacade;
mFacade = facade;
+ mHandler = new Handler(clientModeImplLooper);
mWifiMetrics = wifiMetrics;
+ mClock = clock;
loadSettings();
+
+ mDeviceConfigFacade.addOnPropertiesChangedListener(
+ command -> mHandler.post(command),
+ properties -> {
+ updateUsabilityDataCollectionFlags();
+ });
}
/**
@@ -59,15 +93,18 @@
MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT);
mWifiMetrics.setWifiDataStallMinTxBad(mMinTxBad);
mWifiMetrics.setWifiDataStallMinRxWithoutTx(mMinTxSuccessWithoutRx);
+ updateUsabilityDataCollectionFlags();
}
/**
* Checks for data stall by looking at tx/rx packet counts
* @param oldStats second most recent WifiLinkLayerStats
* @param newStats most recent WifiLinkLayerStats
+ * @param wifiInfo WifiInfo for current connection
* @return trigger type of WifiIsUnusableEvent
*/
- public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats) {
+ public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats,
+ WifiInfo wifiInfo) {
if (oldStats == null || newStats == null) {
mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
return WifiIsUnusableEvent.TYPE_UNKNOWN;
@@ -103,25 +140,130 @@
mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta,
txBadDelta, rxSuccessDelta, timeMsDelta);
+
if (timeMsDelta < MAX_MS_DELTA_FOR_DATA_STALL) {
- // There is a data stall if there are too many tx failures
- // or if we are not receiving any packets despite many tx successes
- boolean dataStallBadTx = (txBadDelta >= mMinTxBad);
- boolean dataStallTxSuccessWithoutRx =
- (rxSuccessDelta == 0 && txSuccessDelta >= mMinTxSuccessWithoutRx);
- if (dataStallBadTx && dataStallTxSuccessWithoutRx) {
- mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH);
- return WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH;
- } else if (dataStallBadTx) {
- mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
- return WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX;
- } else if (dataStallTxSuccessWithoutRx) {
- mWifiMetrics.logWifiIsUnusableEvent(
- WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX);
- return WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX;
+ int txLinkSpeed = wifiInfo.getLinkSpeed();
+ int rxLinkSpeed = wifiInfo.getRxLinkSpeedMbps();
+ boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1
+ || (mLastBssid.equals(wifiInfo.getBSSID())
+ && mLastFrequency == wifiInfo.getFrequency());
+ mLastFrequency = wifiInfo.getFrequency();
+ mLastBssid = wifiInfo.getBSSID();
+
+ int ccaLevel = updateCcaLevel(newStats, wifiInfo, isSameBssidAndFreq);
+ int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq);
+
+ boolean isTxTputLow = false;
+ boolean isRxTputLow = false;
+ if (txLinkSpeed > 0) {
+ int txTput = txLinkSpeed * (100 - txPer) * (100 - ccaLevel);
+ isTxTputLow = txTput < mDataStallTxTputThrMbps * 100 * 100;
+ }
+ if (rxLinkSpeed > 0) {
+ int rxTput = rxLinkSpeed * (100 - ccaLevel);
+ isRxTputLow = rxTput < mDataStallRxTputThrMbps * 100;
+ }
+
+ boolean dataStallTx = isTxTputLow || ccaLevel >= mDataStallCcaLevelThr
+ || txPer >= mDataStallTxPerThr;
+ boolean dataStallRx = isRxTputLow || ccaLevel >= mDataStallCcaLevelThr;
+
+ // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls
+ // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall
+ if (dataStallTx || dataStallRx) {
+ mDataStallTx = mDataStallTx || dataStallTx;
+ mDataStallRx = mDataStallRx || dataStallRx;
+ if (mDataStallStartTimeMs == -1) {
+ mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis();
+ if (mDataStallDurationMs == 0) {
+ mDataStallStartTimeMs = -1;
+ int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx);
+ mDataStallRx = false;
+ mDataStallTx = false;
+ return result;
+ }
+ } else {
+ long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs;
+ if (elapsedTime >= mDataStallDurationMs) {
+ mDataStallStartTimeMs = -1;
+ if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) {
+ int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx);
+ mDataStallRx = false;
+ mDataStallTx = false;
+ return result;
+ } else {
+ mDataStallTx = false;
+ mDataStallRx = false;
+ }
+ } else {
+ // No need to do anything.
+ }
+ }
+ } else {
+ mDataStallStartTimeMs = -1;
+ mDataStallTx = false;
+ mDataStallRx = false;
}
}
return WifiIsUnusableEvent.TYPE_UNKNOWN;
}
+
+ private int updateCcaLevel(WifiLinkLayerStats newStats, WifiInfo wifiInfo,
+ boolean isSameBssidAndFreq) {
+ WifiLinkLayerStats.ChannelStats statsMap = newStats.channelStatsMap.get(mLastFrequency);
+ if (statsMap == null || !isSameBssidAndFreq) {
+ return DEFAULT_CCA_LEVEL;
+ }
+ int radioOnTimeDelta = (int) (statsMap.radioOnTimeMs - mLastTotalRadioOnFreqTimeMs);
+ int ccaBusyTimeDelta = (int) (statsMap.ccaBusyTimeMs - mLastTotalCcaBusyFreqTimeMs);
+ mLastTotalRadioOnFreqTimeMs = statsMap.radioOnTimeMs;
+ mLastTotalCcaBusyFreqTimeMs = statsMap.ccaBusyTimeMs;
+
+ boolean isCcaValid = (radioOnTimeDelta > 0) && (ccaBusyTimeDelta >= 0)
+ && (ccaBusyTimeDelta <= radioOnTimeDelta);
+ // Update CCA level only if CCA stats are valid.
+ if (!isCcaValid) {
+ return DEFAULT_CCA_LEVEL;
+ }
+ return (int) (ccaBusyTimeDelta * 100 / radioOnTimeDelta);
+ }
+
+ private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq) {
+ if (!isSameBssidAndFreq) {
+ return DEFAULT_TX_PACKET_ERROR_RATE;
+ }
+ long txAttempts = txSuccessDelta + txRetriesDelta;
+ if (txAttempts <= 0) {
+ return DEFAULT_TX_PACKET_ERROR_RATE;
+ }
+ return (int) (txRetriesDelta * 100 / txAttempts);
+ }
+
+ private int calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx) {
+ int result = WifiIsUnusableEvent.TYPE_UNKNOWN;
+ if (dataStallTx && dataStallRx) {
+ result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH;
+ } else if (dataStallTx) {
+ result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX;
+ } else if (dataStallRx) {
+ result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX;
+ }
+ mWifiMetrics.logWifiIsUnusableEvent(result);
+ return result;
+ }
+
+ private void updateUsabilityDataCollectionFlags() {
+ mDataStallDurationMs = mDeviceConfigFacade.getDataStallDurationMs();
+ mDataStallTxTputThrMbps = mDeviceConfigFacade.getDataStallTxTputThrMbps();
+ mDataStallRxTputThrMbps = mDeviceConfigFacade.getDataStallRxTputThrMbps();
+ mDataStallTxPerThr = mDeviceConfigFacade.getDataStallTxPerThr();
+ mDataStallCcaLevelThr = mDeviceConfigFacade.getDataStallCcaLevelThr();
+
+ mWifiMetrics.setDataStallDurationMs(mDataStallDurationMs);
+ mWifiMetrics.setDataStallTxTputThrMbps(mDataStallTxTputThrMbps);
+ mWifiMetrics.setDataStallRxTputThrMbps(mDataStallRxTputThrMbps);
+ mWifiMetrics.setDataStallTxPerThr(mDataStallTxPerThr);
+ mWifiMetrics.setDataStallCcaLevelThr(mDataStallCcaLevelThr);
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java
index 52a787a..da1e5b3 100644
--- a/service/java/com/android/server/wifi/WifiDiagnostics.java
+++ b/service/java/com/android/server/wifi/WifiDiagnostics.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.util.ArraySet;
import android.util.Base64;
+import android.util.Log;
import android.util.SparseLongArray;
import com.android.internal.R;
@@ -40,6 +42,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.Deflater;
@@ -124,6 +127,9 @@
private WifiInjector mWifiInjector;
private Clock mClock;
+ /** Interfaces started logging */
+ private final Set<String> mActiveInterfaces = new ArraySet<>();
+
public WifiDiagnostics(Context context, WifiInjector wifiInjector,
WifiNative wifiNative, BuildProperties buildProperties,
LastMileLogger lastMileLogger, Clock clock) {
@@ -148,40 +154,35 @@
mClock = clock;
}
+ /**
+ * Start wifi HAL dependent logging features.
+ * This method should be called only after the interface has
+ * been set up.
+ *
+ * @param ifaceName the interface requesting to start logging.
+ */
@Override
- public synchronized void startLogging(boolean verboseEnabled) {
- mFirmwareVersion = mWifiNative.getFirmwareVersion();
- mDriverVersion = mWifiNative.getDriverVersion();
- mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet();
+ public synchronized void startLogging(@NonNull String ifaceName) {
+ if (mActiveInterfaces.contains(ifaceName)) {
+ Log.w(TAG, "Interface: " + ifaceName + " had already started logging");
+ return;
+ }
+ if (mActiveInterfaces.isEmpty()) {
+ mFirmwareVersion = mWifiNative.getFirmwareVersion();
+ mDriverVersion = mWifiNative.getDriverVersion();
+ mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet();
- if (!mIsLoggingEventHandlerRegistered) {
- mIsLoggingEventHandlerRegistered = mWifiNative.setLoggingEventHandler(mHandler);
+ if (!mIsLoggingEventHandlerRegistered) {
+ mIsLoggingEventHandlerRegistered = mWifiNative.setLoggingEventHandler(mHandler);
+ }
+
+ startLoggingRingBuffers();
}
- if (verboseEnabled) {
- mLogLevel = VERBOSE_LOG_WITH_WAKEUP;
- mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_LARGE;
- } else {
- mLogLevel = VERBOSE_NORMAL_LOG;
- mMaxRingBufferSizeBytes = enableVerboseLoggingForDogfood()
- ? RING_BUFFER_BYTE_LIMIT_LARGE : RING_BUFFER_BYTE_LIMIT_SMALL;
- clearVerboseLogs();
- }
+ mActiveInterfaces.add(ifaceName);
- if (mRingBuffers == null) {
- fetchRingBuffers();
- }
-
- if (mRingBuffers != null) {
- /* log level may have changed, so restart logging with new levels */
- stopLoggingAllBuffers();
- resizeRingBuffers();
- startLoggingAllExceptPerPacketBuffers();
- }
-
- if (!mWifiNative.startPktFateMonitoring(mWifiNative.getClientInterfaceName())) {
- mLog.wC("Failed to start packet fate monitoring");
- }
+ Log.d(TAG, "startLogging() iface list is " + mActiveInterfaces
+ + " after adding " + ifaceName);
}
@Override
@@ -202,8 +203,28 @@
}
}
+ /**
+ * Stop wifi HAL dependent logging features.
+ * This method should be called before the interface has been
+ * torn down.
+ *
+ * @param ifaceName the interface requesting to stop logging.
+ */
@Override
- public synchronized void stopLogging() {
+ public synchronized void stopLogging(@NonNull String ifaceName) {
+ if (!mActiveInterfaces.contains(ifaceName)) {
+ Log.w(TAG, "ifaceName: " + ifaceName + " is not in the start log user list");
+ return;
+ }
+
+ mActiveInterfaces.remove(ifaceName);
+
+ Log.d(TAG, "stopLogging() iface list is " + mActiveInterfaces
+ + " after removing " + ifaceName);
+
+ if (!mActiveInterfaces.isEmpty()) {
+ return;
+ }
if (mIsLoggingEventHandlerRegistered) {
if (!mWifiNative.resetLogHandler()) {
mLog.wC("Fail to reset log handler");
@@ -217,7 +238,6 @@
if (mLogLevel != VERBOSE_NO_LOG) {
stopLoggingAllBuffers();
mRingBuffers = null;
- mLogLevel = VERBOSE_NO_LOG;
}
}
@@ -442,12 +462,33 @@
mWifiMetrics.logFirmwareAlert(errorCode);
}
+ /**
+ * Enables or disables verbose logging
+ *
+ * @param verbose - with the obvious interpretation
+ */
+ @Override
+ public synchronized void enableVerboseLogging(boolean verboseEnabled) {
+ if (verboseEnabled) {
+ mLogLevel = VERBOSE_LOG_WITH_WAKEUP;
+ mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_LARGE;
+ } else {
+ mLogLevel = VERBOSE_NORMAL_LOG;
+ mMaxRingBufferSizeBytes = enableVerboseLoggingForDogfood()
+ ? RING_BUFFER_BYTE_LIMIT_LARGE : RING_BUFFER_BYTE_LIMIT_SMALL;
+ }
+
+ if (!mActiveInterfaces.isEmpty()) {
+ mLog.wC("verbosity changed: restart logging");
+ startLoggingRingBuffers();
+ }
+ }
+
private boolean isVerboseLoggingEnabled() {
return mLogLevel > VERBOSE_NORMAL_LOG;
}
private void clearVerboseLogs() {
- mPacketFatesForLastFailure = null;
for (int i = 0; i < mLastAlerts.size(); i++) {
mLastAlerts.get(i).clearVerboseLogs();
@@ -486,6 +527,21 @@
}
}
+ private void startLoggingRingBuffers() {
+ if (!isVerboseLoggingEnabled()) {
+ clearVerboseLogs();
+ }
+ if (mRingBuffers == null) {
+ fetchRingBuffers();
+ }
+ if (mRingBuffers != null) {
+ // Log level may have changed, so restart logging with new levels.
+ stopLoggingAllBuffers();
+ resizeRingBuffers();
+ startLoggingAllExceptPerPacketBuffers();
+ }
+ }
+
private boolean startLoggingAllExceptPerPacketBuffers() {
if (mRingBuffers == null) {
@@ -743,4 +799,16 @@
pw.println("--------------------------------------------------------------------");
}
+
+ /**
+ * Enable packet fate monitoring.
+ *
+ * @param ifaceName Name of the interface.
+ */
+ @Override
+ public void startPktFateMonitoring(@NonNull String ifaceName) {
+ if (!mWifiNative.startPktFateMonitoring(ifaceName)) {
+ mLog.wC("Failed to start packet fate monitoring");
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index c435ad2..a234d4d 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -20,6 +20,7 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.SystemSensorManager;
@@ -82,6 +83,7 @@
private final Context mContext;
private final FrameworkFacade mFrameworkFacade = new FrameworkFacade();
+ private final DeviceConfigFacade mDeviceConfigFacade;
private final HandlerThread mWifiServiceHandlerThread;
private final HandlerThread mWifiCoreHandlerThread;
private final HandlerThread mWifiP2pServiceHandlerThread;
@@ -153,6 +155,8 @@
private final LinkProbeManager mLinkProbeManager;
private final IpMemoryStore mIpMemoryStore;
private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
+ private final MacAddressUtil mMacAddressUtil;
+ private final ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
public WifiInjector(Context context) {
if (context == null) {
@@ -167,7 +171,11 @@
sWifiInjector = this;
+ mMacAddressUtil = new MacAddressUtil();
mContext = context;
+ mDeviceConfigFacade = new DeviceConfigFacade();
+ mConnectionFailureNotificationBuilder = new ConnectionFailureNotificationBuilder(
+ mContext, getWifiStackPackageName(), mFrameworkFacade);
mWifiScoreCard = new WifiScoreCard(mClock,
Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID));
mSettingsStore = new WifiSettingsStore(mContext);
@@ -202,7 +210,7 @@
mCellularLinkLayerStatsCollector);
// Modules interacting with Native.
mWifiMonitor = new WifiMonitor(this);
- mHalDeviceManager = new HalDeviceManager(mClock);
+ mHalDeviceManager = new HalDeviceManager(mClock, clientModeImplLooper);
mWifiVendorHal =
new WifiVendorHal(mHalDeviceManager, mWifiCoreHandlerThread.getLooper());
mSupplicantStaIfaceHal =
@@ -239,7 +247,7 @@
mWifiKeyStore = new WifiKeyStore(mKeyStore);
mWifiConfigStore = new WifiConfigStore(
mContext, clientModeImplLooper, mClock, mWifiMetrics,
- WifiConfigStore.createSharedFile());
+ WifiConfigStore.createSharedFile(mFrameworkFacade.isNiapModeOn(mContext)));
SubscriptionManager subscriptionManager =
mContext.getSystemService(SubscriptionManager.class);
// Config Manager
@@ -249,7 +257,7 @@
mWifiPermissionsWrapper, this, new NetworkListSharedStoreData(mContext),
new NetworkListUserStoreData(mContext),
new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(),
- mFrameworkFacade, mWifiCoreHandlerThread.getLooper());
+ mFrameworkFacade, mWifiCoreHandlerThread.getLooper(), mDeviceConfigFacade);
mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
mConnectivityLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512);
@@ -299,7 +307,8 @@
mWifiDiagnostics = new WifiDiagnostics(
mContext, this, mWifiNative, mBuildProperties,
new LastMileLogger(this), mClock);
- mWifiDataStall = new WifiDataStall(mContext, mFrameworkFacade, mWifiMetrics);
+ mWifiDataStall = new WifiDataStall(mContext, mFrameworkFacade, mWifiMetrics,
+ mDeviceConfigFacade, clientModeImplLooper, mClock);
mWifiMetrics.setWifiDataStall(mWifiDataStall);
mLinkProbeManager = new LinkProbeManager(mClock, mWifiNative, mWifiMetrics,
mFrameworkFacade, mWifiCoreHandlerThread.getLooper(), mContext);
@@ -535,7 +544,7 @@
@NonNull SoftApModeConfiguration config) {
return new SoftApManager(mContext, mWifiCoreHandlerThread.getLooper(),
mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), callback,
- mWifiApConfigStore, config, mWifiMetrics, mSarManager);
+ mWifiApConfigStore, config, mWifiMetrics, mSarManager, mWifiDiagnostics);
}
/**
@@ -605,8 +614,9 @@
mWifiCoreHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics,
mWifiConfigManager, mWifiConfigStore, clientModeImpl,
new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
- mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mClock,
- mWifiMetrics, clientModeImpl, clientModeImpl.getHandler().getLooper());
+ mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock,
+ mWifiMetrics, clientModeImpl, clientModeImpl.getHandler().getLooper(),
+ mDeviceConfigFacade);
return new WifiConnectivityManager(mContext, getScoringParams(),
clientModeImpl, this,
mWifiConfigManager, clientModeImpl.getWifiInfo(),
@@ -617,6 +627,17 @@
}
/**
+ * Construct a new instance of ConnectionFailureNotifier.
+ * @param wifiConnectivityManager
+ * @return the created instance
+ */
+ public ConnectionFailureNotifier makeConnectionFailureNotifier(
+ WifiConnectivityManager wifiConnectivityManager) {
+ return new ConnectionFailureNotifier(mContext, this, mFrameworkFacade, mWifiConfigManager,
+ wifiConnectivityManager, new Handler(mWifiCoreHandlerThread.getLooper()));
+ }
+
+ /**
* Construct a new instance of {@link WifiNetworkFactory}.
* TODO(b/116233964): Remove cyclic dependency between WifiConnectivityManager & ClientModeImpl.
*/
@@ -691,6 +712,18 @@
return mRttHandlerThread;
}
+ public MacAddressUtil getMacAddressUtil() {
+ return mMacAddressUtil;
+ }
+
+ public NotificationManager getNotificationManager() {
+ return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ public ConnectionFailureNotificationBuilder getConnectionFailureNotificationBuilder() {
+ return mConnectionFailureNotificationBuilder;
+ }
+
/**
* Returns a single instance of HalDeviceManager for injection.
*/
@@ -745,4 +778,12 @@
public IpMemoryStore getIpMemoryStore() {
return mIpMemoryStore;
}
+
+ public HostapdHal getHostapdHal() {
+ return mHostapdHal;
+ }
+
+ public String getWifiStackPackageName() {
+ return mContext.getPackageName();
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
index 6889b50..e8a0688 100644
--- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
+++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
@@ -16,10 +16,12 @@
package com.android.server.wifi;
+import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -75,6 +77,10 @@
@VisibleForTesting
public static final long LAST_TRIGGER_TIMEOUT_MILLIS = 2 * 3600 * 1000; // 2 hours
+ private int mAbnormalConnectionDurationMs;
+ private boolean mAbnormalConnectionBugreportEnabled;
+
+
/**
* Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results
* Key:BSSID, Value:Counters of failure types
@@ -102,22 +108,95 @@
private Looper mClientModeImplLooper;
private double mBugReportProbability = PROB_TAKE_BUGREPORT_DEFAULT;
private Clock mClock;
+ private Context mContext;
+ private DeviceConfigFacade mDeviceConfigFacade;
// If any connection failure happened after watchdog triggering restart then assume watchdog
// did not fix the problem
private boolean mWatchdogFixedWifi = true;
+ private long mLastStartConnectTime = 0;
+ private Handler mHandler;
/**
* Local log used for debugging any WifiLastResortWatchdog issues.
*/
private final LocalLog mLocalLog = new LocalLog(100);
- WifiLastResortWatchdog(WifiInjector wifiInjector, Clock clock, WifiMetrics wifiMetrics,
- ClientModeImpl clientModeImpl, Looper clientModeImplLooper) {
+ WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock,
+ WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper,
+ DeviceConfigFacade deviceConfigFacade) {
mWifiInjector = wifiInjector;
mClock = clock;
mWifiMetrics = wifiMetrics;
mClientModeImpl = clientModeImpl;
mClientModeImplLooper = clientModeImplLooper;
+ mContext = context;
+ mDeviceConfigFacade = deviceConfigFacade;
+ updateDeviceConfigFlags();
+ mHandler = new Handler(clientModeImplLooper) {
+ public void handleMessage(Message msg) {
+ processMessage(msg);
+ }
+ };
+
+ mDeviceConfigFacade.addOnPropertiesChangedListener(
+ command -> mHandler.post(command),
+ properties -> {
+ updateDeviceConfigFlags();
+ });
+ }
+
+ private void updateDeviceConfigFlags() {
+ mAbnormalConnectionBugreportEnabled =
+ mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled();
+ mAbnormalConnectionDurationMs =
+ mDeviceConfigFacade.getAbnormalConnectionDurationMs();
+ logv("updateDeviceConfigFlags: mAbnormalConnectionDurationMs = "
+ + mAbnormalConnectionDurationMs
+ + ", mAbnormalConnectionBugreportEnabled = "
+ + mAbnormalConnectionBugreportEnabled);
+ }
+
+ /**
+ * Returns handler for L2 events from supplicant.
+ * @return Handler
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * Refreshes when the last CMD_START_CONNECT is triggered.
+ */
+ public void noteStartConnectTime() {
+ mHandler.post(() -> {
+ mLastStartConnectTime = mClock.getElapsedSinceBootMillis();
+ });
+ }
+
+ private void processMessage(Message msg) {
+ switch (msg.what) {
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ // Trigger bugreport for successful connections that take abnormally long
+ if (mAbnormalConnectionBugreportEnabled && mLastStartConnectTime > 0) {
+ long durationMs = mClock.getElapsedSinceBootMillis() - mLastStartConnectTime;
+ if (durationMs > mAbnormalConnectionDurationMs) {
+ final String bugTitle = "Wi-Fi Bugreport: Abnormal connection time";
+ final String bugDetail = "Expected connection to take less than "
+ + mAbnormalConnectionDurationMs + " milliseconds. "
+ + "Actually took " + durationMs + " milliseconds.";
+ logv("Triggering bug report for abnormal connection time.");
+ mWifiInjector.getClientModeImplHandler().post(() -> {
+ mClientModeImpl.takeBugReport(bugTitle, bugDetail);
+ });
+ }
+ }
+ // Should reset last connection time after each connection regardless if bugreport
+ // is enabled or not.
+ mLastStartConnectTime = 0;
+ break;
+ default:
+ return;
+ }
}
/**
diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java
index e292a84..45e6882 100644
--- a/service/java/com/android/server/wifi/WifiLockManager.java
+++ b/service/java/com/android/server/wifi/WifiLockManager.java
@@ -183,10 +183,6 @@
* @return true if the lock was successfully acquired, false if the lockMode was invalid.
*/
public boolean acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
- if (!isValidLockMode(lockMode)) {
- throw new IllegalArgumentException("lockMode =" + lockMode);
- }
-
// Make a copy of the WorkSource before adding it to the WakeLock
// This is to make sure worksource value can not be changed by caller
// after function returns.
@@ -384,7 +380,13 @@
}
}
- private static boolean isValidLockMode(int lockMode) {
+ /**
+ * Validate that the lock mode is valid - i.e. one of the supported enumerations.
+ *
+ * @param lockMode The lock mode to verify.
+ * @return true for valid lock modes, false otherwise.
+ */
+ public static boolean isValidLockMode(int lockMode) {
if (lockMode != WifiManager.WIFI_MODE_FULL
&& lockMode != WifiManager.WIFI_MODE_SCAN_ONLY
&& lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index a62ad37..6db4e99 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -26,6 +26,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.DeviceMobilityState;
@@ -180,6 +181,11 @@
// Maximum time that a score breaching low event stays valid.
public static final int VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS = 90 * 1000; // 1.5 minutes
+ public static final int BAND_2G_MAX_FREQ_MHZ = 2484;
+ public static final int BAND_5G_LOW_MAX_FREQ_MHZ = 5240;
+ public static final int BAND_5G_MID_MAX_FREQ_MHZ = 5720;
+ public static final int BAND_5G_HIGH_MAX_FREQ_MHZ = 5865;
+
private Clock mClock;
private boolean mScreenOn;
private int mWifiState;
@@ -216,6 +222,7 @@
private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<>();
private int mLastPollRssi = -127;
private int mLastPollLinkSpeed = -1;
+ private int mLastPollRxLinkSpeed = -1;
private int mLastPollFreq = -1;
private int mLastScore = -1;
@@ -254,6 +261,16 @@
private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
/** Mapping of link speed values to LinkSpeedCount objects. */
private final SparseArray<LinkSpeedCount> mLinkSpeedCounts = new SparseArray<>();
+
+ private final IntCounter mTxLinkSpeedCount2g = new IntCounter();
+ private final IntCounter mTxLinkSpeedCount5gLow = new IntCounter();
+ private final IntCounter mTxLinkSpeedCount5gMid = new IntCounter();
+ private final IntCounter mTxLinkSpeedCount5gHigh = new IntCounter();
+ private final IntCounter mRxLinkSpeedCount2g = new IntCounter();
+ private final IntCounter mRxLinkSpeedCount5gLow = new IntCounter();
+ private final IntCounter mRxLinkSpeedCount5gMid = new IntCounter();
+ private final IntCounter mRxLinkSpeedCount5gHigh = new IntCounter();
+
/** RSSI of the scan result for the last connection event*/
private int mScanResultRssi = 0;
/** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
@@ -464,6 +481,8 @@
sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
+ sb.append(", mEapMethod=" + mRouterFingerPrintProto.eapMethod);
+ sb.append(", mAuthPhase2Method=" + mRouterFingerPrintProto.authPhase2Method);
}
return sb.toString();
}
@@ -500,9 +519,61 @@
if (candidate != null) {
updateMetricsFromScanResult(candidate);
}
+ if (mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
+ .authentication == WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE
+ && config.enterpriseConfig != null) {
+ int eapMethod = config.enterpriseConfig.getEapMethod();
+ mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
+ .eapMethod = getEapMethodProto(eapMethod);
+ int phase2Method = config.enterpriseConfig.getPhase2Method();
+ mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
+ .authPhase2Method = getAuthPhase2MethodProto(phase2Method);
+ }
}
}
}
+ private int getEapMethodProto(int eapMethod) {
+ switch (eapMethod) {
+ case WifiEnterpriseConfig.Eap.TLS:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_TLS;
+ case WifiEnterpriseConfig.Eap.UNAUTH_TLS:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_UNAUTH_TLS;
+ case WifiEnterpriseConfig.Eap.PEAP:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_PEAP;
+ case WifiEnterpriseConfig.Eap.PWD:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_PWD;
+ case WifiEnterpriseConfig.Eap.TTLS:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_TTLS;
+ case WifiEnterpriseConfig.Eap.SIM:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_SIM;
+ case WifiEnterpriseConfig.Eap.AKA:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_AKA;
+ case WifiEnterpriseConfig.Eap.AKA_PRIME:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_AKA_PRIME;
+ default:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_UNKNOWN;
+ }
+ }
+ private int getAuthPhase2MethodProto(int phase2Method) {
+ switch (phase2Method) {
+ case WifiEnterpriseConfig.Phase2.PAP:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_PAP;
+ case WifiEnterpriseConfig.Phase2.MSCHAP:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_MSCHAP;
+ case WifiEnterpriseConfig.Phase2.MSCHAPV2:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_MSCHAPV2;
+ case WifiEnterpriseConfig.Phase2.GTC:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_GTC;
+ case WifiEnterpriseConfig.Phase2.SIM:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_SIM;
+ case WifiEnterpriseConfig.Phase2.AKA:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_AKA;
+ case WifiEnterpriseConfig.Phase2.AKA_PRIME:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_AKA_PRIME;
+ default:
+ return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_NONE;
+ }
+ }
}
/**
@@ -1527,6 +1598,9 @@
mLastPollFreq = wifiInfo.getFrequency();
incrementRssiPollRssiCount(mLastPollFreq, mLastPollRssi);
incrementLinkSpeedCount(mLastPollLinkSpeed, mLastPollRssi);
+ mLastPollRxLinkSpeed = wifiInfo.getRxLinkSpeedMbps();
+ incrementTxLinkSpeedBandCount(mLastPollLinkSpeed, mLastPollFreq);
+ incrementRxLinkSpeedBandCount(mLastPollRxLinkSpeed, mLastPollFreq);
}
/**
@@ -1596,6 +1670,56 @@
}
/**
+ * Increment occurrence count of Tx link speed for operating sub-band
+ * Ignores link speed values that are lower than MIN_LINK_SPEED_MBPS
+ * @param txLinkSpeed PHY layer Tx link speed in Mbps
+ * @param frequency Channel frequency of beacon frames in MHz
+ */
+ @VisibleForTesting
+ public void incrementTxLinkSpeedBandCount(int txLinkSpeed, int frequency) {
+ if (!(mLinkSpeedCountsLogging
+ && txLinkSpeed >= MIN_LINK_SPEED_MBPS)) {
+ return;
+ }
+ synchronized (mLock) {
+ if (frequency <= BAND_2G_MAX_FREQ_MHZ) {
+ mTxLinkSpeedCount2g.increment(txLinkSpeed);
+ } else if (frequency <= BAND_5G_LOW_MAX_FREQ_MHZ) {
+ mTxLinkSpeedCount5gLow.increment(txLinkSpeed);
+ } else if (frequency <= BAND_5G_MID_MAX_FREQ_MHZ) {
+ mTxLinkSpeedCount5gMid.increment(txLinkSpeed);
+ } else {
+ mTxLinkSpeedCount5gHigh.increment(txLinkSpeed);
+ }
+ }
+ }
+
+ /**
+ * Increment occurrence count of Rx link speed for operating sub-band
+ * Ignores link speed values that are lower than MIN_LINK_SPEED_MBPS
+ * @param rxLinkSpeed PHY layer Tx link speed in Mbps
+ * @param frequency Channel frequency of beacon frames in MHz
+ */
+ @VisibleForTesting
+ public void incrementRxLinkSpeedBandCount(int rxLinkSpeed, int frequency) {
+ if (!(mLinkSpeedCountsLogging
+ && rxLinkSpeed >= MIN_LINK_SPEED_MBPS)) {
+ return;
+ }
+ synchronized (mLock) {
+ if (frequency <= BAND_2G_MAX_FREQ_MHZ) {
+ mRxLinkSpeedCount2g.increment(rxLinkSpeed);
+ } else if (frequency <= BAND_5G_LOW_MAX_FREQ_MHZ) {
+ mRxLinkSpeedCount5gLow.increment(rxLinkSpeed);
+ } else if (frequency <= BAND_5G_MID_MAX_FREQ_MHZ) {
+ mRxLinkSpeedCount5gMid.increment(rxLinkSpeed);
+ } else {
+ mRxLinkSpeedCount5gHigh.increment(rxLinkSpeed);
+ }
+ }
+ }
+
+ /**
* Increment count of Watchdog successes.
*/
public void incrementNumLastResortWatchdogSuccesses() {
@@ -2713,6 +2837,16 @@
+ mExperimentValues.wifiDataStallMinTxSuccessWithoutRx);
pw.println("mExperimentValues.linkSpeedCountsLoggingEnabled="
+ mExperimentValues.linkSpeedCountsLoggingEnabled);
+ pw.println("mExperimentValues.dataStallDurationMs="
+ + mExperimentValues.dataStallDurationMs);
+ pw.println("mExperimentValues.dataStallTxTputThrMbps="
+ + mExperimentValues.dataStallTxTputThrMbps);
+ pw.println("mExperimentValues.dataStallRxTputThrMbps="
+ + mExperimentValues.dataStallRxTputThrMbps);
+ pw.println("mExperimentValues.dataStallTxPerThr="
+ + mExperimentValues.dataStallTxPerThr);
+ pw.println("mExperimentValues.dataStallCcaLevelThr="
+ + mExperimentValues.dataStallCcaLevelThr);
pw.println("WifiIsUnusableEventList: ");
for (WifiIsUnusableWithTime event : mWifiIsUnusableList) {
pw.println(event);
@@ -2793,6 +2927,15 @@
+ mWifiLogProto.numAddOrUpdateNetworkCalls);
pw.println("mWifiLogProto.numEnableNetworkCalls="
+ mWifiLogProto.numEnableNetworkCalls);
+
+ pw.println("mWifiLogProto.txLinkSpeedCount2g=" + mTxLinkSpeedCount2g);
+ pw.println("mWifiLogProto.txLinkSpeedCount5gLow=" + mTxLinkSpeedCount5gLow);
+ pw.println("mWifiLogProto.txLinkSpeedCount5gMid=" + mTxLinkSpeedCount5gMid);
+ pw.println("mWifiLogProto.txLinkSpeedCount5gHigh=" + mTxLinkSpeedCount5gHigh);
+ pw.println("mWifiLogProto.rxLinkSpeedCount2g=" + mRxLinkSpeedCount2g);
+ pw.println("mWifiLogProto.rxLinkSpeedCount5gLow=" + mRxLinkSpeedCount5gLow);
+ pw.println("mWifiLogProto.rxLinkSpeedCount5gMid=" + mRxLinkSpeedCount5gMid);
+ pw.println("mWifiLogProto.rxLinkSpeedCount5gHigh=" + mRxLinkSpeedCount5gHigh);
}
}
}
@@ -2854,6 +2997,7 @@
public void updateSavedNetworks(List<WifiConfiguration> networks) {
synchronized (mLock) {
mWifiLogProto.numSavedNetworks = networks.size();
+ mWifiLogProto.numSavedNetworksWithMacRandomization = 0;
mWifiLogProto.numOpenNetworks = 0;
mWifiLogProto.numLegacyPersonalNetworks = 0;
mWifiLogProto.numLegacyEnterpriseNetworks = 0;
@@ -3337,6 +3481,15 @@
entry.count = count;
return entry;
});
+ // 'G' is due to that 1st Letter after _ becomes capital during protobuff compilation
+ mWifiLogProto.txLinkSpeedCount2G = mTxLinkSpeedCount2g.toProto();
+ mWifiLogProto.txLinkSpeedCount5GLow = mTxLinkSpeedCount5gLow.toProto();
+ mWifiLogProto.txLinkSpeedCount5GMid = mTxLinkSpeedCount5gMid.toProto();
+ mWifiLogProto.txLinkSpeedCount5GHigh = mTxLinkSpeedCount5gHigh.toProto();
+ mWifiLogProto.rxLinkSpeedCount2G = mRxLinkSpeedCount2g.toProto();
+ mWifiLogProto.rxLinkSpeedCount5GLow = mRxLinkSpeedCount5gLow.toProto();
+ mWifiLogProto.rxLinkSpeedCount5GMid = mRxLinkSpeedCount5gMid.toProto();
+ mWifiLogProto.rxLinkSpeedCount5GHigh = mRxLinkSpeedCount5gHigh.toProto();
}
}
@@ -3440,6 +3593,14 @@
mRssiPollCountsMap.clear();
mRssiDeltaCounts.clear();
mLinkSpeedCounts.clear();
+ mTxLinkSpeedCount2g.clear();
+ mTxLinkSpeedCount5gLow.clear();
+ mTxLinkSpeedCount5gMid.clear();
+ mTxLinkSpeedCount5gHigh.clear();
+ mRxLinkSpeedCount2g.clear();
+ mRxLinkSpeedCount5gLow.clear();
+ mRxLinkSpeedCount5gMid.clear();
+ mRxLinkSpeedCount5gHigh.clear();
mWifiAlertReasonCounts.clear();
mWifiScoreCounts.clear();
mWifiUsabilityScoreCounts.clear();
@@ -3681,12 +3842,15 @@
mLastPollRssi = -127;
mLastPollFreq = -1;
mLastPollLinkSpeed = -1;
+ mLastPollRxLinkSpeed = -1;
mLastScore = -1;
mLastWifiUsabilityScore = -1;
mLastPredictionHorizonSec = -1;
- mStaEventList.add(new StaEventWithTime(staEvent, mClock.getWallClockMillis()));
- // Prune StaEventList if it gets too long
- if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
+ synchronized (mLock) {
+ mStaEventList.add(new StaEventWithTime(staEvent, mClock.getWallClockMillis()));
+ // Prune StaEventList if it gets too long
+ if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
+ }
}
private ConfigInfo createConfigInfo(WifiConfiguration config) {
@@ -4518,6 +4682,7 @@
}
}
mWifiUsabilityStatsCounter = 0;
+ mWifiUsabilityStatsEntriesList.clear();
}
}
@@ -5095,4 +5260,49 @@
mNumProvisionSuccess++;
}
}
+
+ /**
+ * Sets the duration for evaluating Wifi condition to trigger a data stall
+ */
+ public void setDataStallDurationMs(int duration) {
+ synchronized (mLock) {
+ mExperimentValues.dataStallDurationMs = duration;
+ }
+ }
+
+ /**
+ * Sets the threshold of Tx throughput below which to trigger a data stall
+ */
+ public void setDataStallTxTputThrMbps(int txTputThr) {
+ synchronized (mLock) {
+ mExperimentValues.dataStallTxTputThrMbps = txTputThr;
+ }
+ }
+
+ /**
+ * Sets the threshold of Rx throughput below which to trigger a data stall
+ */
+ public void setDataStallRxTputThrMbps(int rxTputThr) {
+ synchronized (mLock) {
+ mExperimentValues.dataStallRxTputThrMbps = rxTputThr;
+ }
+ }
+
+ /**
+ * Sets the threshold of Tx packet error rate above which to trigger a data stall
+ */
+ public void setDataStallTxPerThr(int txPerThr) {
+ synchronized (mLock) {
+ mExperimentValues.dataStallTxPerThr = txPerThr;
+ }
+ }
+
+ /**
+ * Sets the threshold of CCA level above which to trigger a data stall
+ */
+ public void setDataStallCcaLevelThr(int ccaLevel) {
+ synchronized (mLock) {
+ mExperimentValues.dataStallCcaLevelThr = ccaLevel;
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index ecc1a9f..4b5866b 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -68,6 +68,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -81,6 +82,8 @@
@VisibleForTesting
private static final int SCORE_FILTER = 60;
@VisibleForTesting
+ public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 20 * 1000; // 20 seconds
+ @VisibleForTesting
public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds
@VisibleForTesting
public static final int NETWORK_CONNECTION_TIMEOUT_MS = 30 * 1000; // 30 seconds
@@ -97,6 +100,9 @@
@VisibleForTesting
public static final String UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK =
"com.android.settings.wifi.extra.REQUEST_IS_FOR_SINGLE_NETWORK";
+ // Capacity limit of approved Access Point per App
+ @VisibleForTesting
+ public static final int NUM_OF_ACCESS_POINT_LIMIT_PER_APP = 50;
private final Context mContext;
private final ActivityManager mActivityManager;
@@ -117,8 +123,8 @@
private final ExternalCallbackTracker<INetworkRequestMatchCallback> mRegisteredCallbacks;
private final Messenger mSrcMessenger;
// Store all user approved access points for apps.
- // TODO(b/122658039): Persist this.
- private final Map<String, Set<AccessPoint>> mUserApprovedAccessPointMap = new HashMap<>();
+ @VisibleForTesting
+ public final Map<String, LinkedHashSet<AccessPoint>> mUserApprovedAccessPointMap;
private WifiScanner mWifiScanner;
private int mGenericConnectionReqCount = 0;
@@ -226,37 +232,10 @@
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received " + scanResults.length + " scan results");
}
- List<ScanResult> matchedScanResults =
- getNetworksMatchingActiveNetworkRequest(scanResults);
- if (mActiveMatchedScanResults == null) {
- // only note the first match size in metrics (chances of this changing in further
- // scans is pretty low)
- mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram(
- matchedScanResults.size());
- }
- mActiveMatchedScanResults = matchedScanResults;
-
- ScanResult approvedScanResult = null;
- if (isActiveRequestForSingleAccessPoint()) {
- approvedScanResult =
- findUserApprovedAccessPointForActiveRequestFromActiveMatchedScanResults();
- }
- if (approvedScanResult != null
- && !mWifiConfigManager.wasEphemeralNetworkDeleted(
- ScanResultUtil.createQuotedSSID(approvedScanResult.SSID))) {
- Log.v(TAG, "Approved access point found in matching scan results. "
- + "Triggering connect " + approvedScanResult);
- handleConnectToNetworkUserSelectionInternal(
- ScanResultUtil.createNetworkFromScanResult(approvedScanResult));
- mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass();
- // TODO (b/122658039): Post notification.
- } else {
- if (mVerboseLoggingEnabled) {
- Log.v(TAG, "No approved access points found in matching scan results. "
- + "Sending match callback");
- }
- sendNetworkRequestMatchCallbacksForActiveRequest(matchedScanResults);
- // Didn't find an approved match, schedule the next scan.
+ if (!handleScanResultsAndTriggerConnectIfUserApprovedMatchFound(scanResults)) {
+ // Didn't find an approved match, send the matching results to UI and schedule the
+ // next scan.
+ sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults);
scheduleNextPeriodicScan();
}
}
@@ -346,12 +325,13 @@
public Map<String, Set<AccessPoint>> toSerialize() {
// Clear the flag after writing to disk.
mHasNewDataToSerialize = false;
- return mUserApprovedAccessPointMap;
+ return new HashMap<>(mUserApprovedAccessPointMap);
}
@Override
public void fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPointMap) {
- mUserApprovedAccessPointMap.putAll(approvedAccessPointMap);
+ approvedAccessPointMap.forEach((key, value) ->
+ mUserApprovedAccessPointMap.put(key, new LinkedHashSet<>(value)));
}
@Override
@@ -397,6 +377,7 @@
mConnectionTimeoutAlarmListener = new ConnectionTimeoutAlarmListener();
mRegisteredCallbacks = new ExternalCallbackTracker<INetworkRequestMatchCallback>(mHandler);
mSrcMessenger = new Messenger(new Handler(looper, mNetworkConnectionTriggerCallback));
+ mUserApprovedAccessPointMap = new HashMap<>();
// register the data store for serializing/deserializing data.
configStore.registerStoreData(
@@ -447,7 +428,12 @@
new NetworkFactoryUserSelectionCallback(mActiveSpecificNetworkRequest));
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke user selection registration callback " + callback, e);
+ return;
}
+
+ // If we are already in the midst of processing a request, send matching callbacks
+ // immediately on registering the callback.
+ sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults);
}
/**
@@ -606,10 +592,19 @@
wns.requestorUid, wns.requestorPackageName);
mWifiMetrics.incrementNetworkRequestApiNumRequest();
- // Start UI to let the user grant/disallow this request from the app.
- startUi();
- // Trigger periodic scans for finding a network in the request.
- startPeriodicScans();
+ // Fetch the latest cached scan results to speed up network matching.
+ ScanResult[] cachedScanResults = getFilteredCachedScanResults();
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Using cached " + cachedScanResults.length + " scan results");
+ }
+ if (!handleScanResultsAndTriggerConnectIfUserApprovedMatchFound(cachedScanResults)) {
+ // Start UI to let the user grant/disallow this request from the app.
+ startUi();
+ // Didn't find an approved match, send the matching results to UI and trigger
+ // periodic scans for finding a network in the request.
+ sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults);
+ startPeriodicScans();
+ }
}
}
@@ -706,6 +701,11 @@
WifiConfiguration existingSavedNetwork =
mWifiConfigManager.getConfiguredNetwork(network.configKey());
if (existingSavedNetwork != null) {
+ if (WifiConfigurationUtil.hasCredentialChanged(existingSavedNetwork, network)) {
+ // TODO (b/142035508): What if the user has a saved network with different
+ // credentials?
+ Log.w(TAG, "Network config already present in config manager, reusing");
+ }
return existingSavedNetwork.networkId;
}
NetworkUpdateResult networkUpdateResult =
@@ -718,6 +718,32 @@
return networkUpdateResult.netId;
}
+ // Helper method to remove the provided network configuration from WifiConfigManager, if it was
+ // added by an app's specifier request.
+ private void disconnectAndRemoveNetworkFromWifiConfigManager(
+ @Nullable WifiConfiguration network) {
+ // Trigger a disconnect first.
+ mWifiInjector.getClientModeImpl().disconnectCommand();
+
+ if (network == null) return;
+ WifiConfiguration wcmNetwork =
+ mWifiConfigManager.getConfiguredNetwork(network.configKey());
+ if (wcmNetwork == null) {
+ Log.e(TAG, "Network not present in config manager");
+ return;
+ }
+ // Remove the network if it was added previously by an app's specifier request.
+ if (wcmNetwork.ephemeral && wcmNetwork.fromWifiNetworkSpecifier) {
+ boolean success =
+ mWifiConfigManager.removeNetwork(wcmNetwork.networkId, wcmNetwork.creatorUid);
+ if (!success) {
+ Log.e(TAG, "Failed to remove network from config manager");
+ } else if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removed network from config manager " + wcmNetwork.networkId);
+ }
+ }
+ }
+
// Helper method to trigger a connection request & schedule a timeout alarm to track the
// connection request.
private void connectToNetwork(@NonNull WifiConfiguration network) {
@@ -756,7 +782,6 @@
networkToConnect.SSID = network.SSID;
// Set the WifiConfiguration.BSSID field to prevent roaming.
networkToConnect.BSSID = findBestBssidFromActiveMatchedScanResultsForNetwork(network);
- // Mark the network ephemeral so that it's automatically removed at the end of connection.
networkToConnect.ephemeral = true;
networkToConnect.fromWifiNetworkSpecifier = true;
@@ -764,7 +789,8 @@
mUserSelectedNetwork = networkToConnect;
// Disconnect from the current network before issuing a new connect request.
- mWifiInjector.getClientModeImpl().disconnectCommand();
+ disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork);
+
// Trigger connection to the network.
connectToNetwork(networkToConnect);
// Triggered connection to network, now wait for the connection status.
@@ -960,6 +986,7 @@
mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier;
mActiveSpecificNetworkRequest = null;
mActiveSpecificNetworkRequestSpecifier = null;
+ mActiveMatchedScanResults = null;
mPendingConnectionSuccess = false;
// Cancel connection timeout alarm.
cancelConnectionTimeout();
@@ -968,7 +995,7 @@
// Invoked at the termination of current connected request processing.
private void teardownForConnectedNetwork() {
Log.i(TAG, "Disconnecting from network on reset");
- mWifiInjector.getClientModeImpl().disconnectCommand();
+ disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork);
mConnectedSpecificNetworkRequest = null;
mConnectedSpecificNetworkRequestSpecifier = null;
// ensure there is no active request in progress.
@@ -1106,7 +1133,8 @@
}
private void sendNetworkRequestMatchCallbacksForActiveRequest(
- List<ScanResult> matchedScanResults) {
+ @Nullable List<ScanResult> matchedScanResults) {
+ if (matchedScanResults == null || matchedScanResults.isEmpty()) return;
if (mRegisteredCallbacks.getNumCallbacks() == 0) {
Log.e(TAG, "No callback registered for sending network request matches. "
+ "Ignoring...");
@@ -1135,10 +1163,11 @@
mConnectionTimeoutSet = true;
}
- private @NonNull CharSequence getAppName(@NonNull String packageName) {
+ private @NonNull CharSequence getAppName(@NonNull String packageName, int uid) {
ApplicationInfo applicationInfo = null;
try {
- applicationInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0, UserHandle.getUserId(uid));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to find app name for " + packageName);
return "";
@@ -1153,7 +1182,8 @@
intent.addCategory(UI_START_INTENT_CATEGORY);
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(UI_START_INTENT_EXTRA_APP_NAME,
- getAppName(mActiveSpecificNetworkRequestSpecifier.requestorPackageName));
+ getAppName(mActiveSpecificNetworkRequestSpecifier.requestorPackageName,
+ mActiveSpecificNetworkRequestSpecifier.requestorUid));
intent.putExtra(UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK,
isActiveRequestForSingleNetwork());
mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(
@@ -1235,6 +1265,9 @@
new AccessPoint(scanResult.SSID,
MacAddress.fromString(scanResult.BSSID), fromScanResult.networkType);
if (approvedAccessPoints.contains(accessPoint)) {
+ // keep the most recently used AP in the end
+ approvedAccessPoints.remove(accessPoint);
+ approvedAccessPoints.add(accessPoint);
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Found " + accessPoint
+ " in user approved access point for " + requestorPackageName);
@@ -1254,10 +1287,11 @@
// object representing an entire network from UI, we need to ensure that all the visible
// BSSIDs matching the original request and the selected network are stored.
Set<AccessPoint> newUserApprovedAccessPoints = new HashSet<>();
+
+ ScanResultMatchInfo fromWifiConfiguration =
+ ScanResultMatchInfo.fromWifiConfiguration(network);
for (ScanResult scanResult : mActiveMatchedScanResults) {
ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult);
- ScanResultMatchInfo fromWifiConfiguration =
- ScanResultMatchInfo.fromWifiConfiguration(network);
if (fromScanResult.equals(fromWifiConfiguration)) {
AccessPoint approvedAccessPoint =
new AccessPoint(scanResult.SSID, MacAddress.fromString(scanResult.BSSID),
@@ -1268,10 +1302,10 @@
if (newUserApprovedAccessPoints.isEmpty()) return;
String requestorPackageName = mActiveSpecificNetworkRequestSpecifier.requestorPackageName;
- Set<AccessPoint> approvedAccessPoints =
+ LinkedHashSet<AccessPoint> approvedAccessPoints =
mUserApprovedAccessPointMap.get(requestorPackageName);
if (approvedAccessPoints == null) {
- approvedAccessPoints = new HashSet<>();
+ approvedAccessPoints = new LinkedHashSet<>();
mUserApprovedAccessPointMap.put(requestorPackageName, approvedAccessPoints);
// Note the new app in metrics.
mWifiMetrics.incrementNetworkRequestApiNumApps();
@@ -1280,22 +1314,92 @@
Log.v(TAG, "Adding " + newUserApprovedAccessPoints
+ " to user approved access point for " + requestorPackageName);
}
+ // keep the most recently added APs in the end
+ approvedAccessPoints.removeAll(newUserApprovedAccessPoints);
approvedAccessPoints.addAll(newUserApprovedAccessPoints);
+ cleanUpLRUAccessPoints(approvedAccessPoints);
saveToStore();
}
/**
+ * Handle scan results
+ * a) Find all scan results matching the active network request.
+ * b) If the request is for a single bssid, check if the matching ScanResult was pre-approved
+ * by the user.
+ * c) If yes to (b), trigger a connect immediately and returns true. Else, returns false.
+ *
+ * @param scanResults Array of {@link ScanResult} to be processed.
+ * @return true if a pre-approved network was found for connection, false otherwise.
+ */
+ private boolean handleScanResultsAndTriggerConnectIfUserApprovedMatchFound(
+ ScanResult[] scanResults) {
+ List<ScanResult> matchedScanResults =
+ getNetworksMatchingActiveNetworkRequest(scanResults);
+ if ((mActiveMatchedScanResults == null || mActiveMatchedScanResults.isEmpty())
+ && !matchedScanResults.isEmpty()) {
+ // only note the first match size in metrics (chances of this changing in further
+ // scans is pretty low)
+ mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram(
+ matchedScanResults.size());
+ }
+ mActiveMatchedScanResults = matchedScanResults;
+
+ ScanResult approvedScanResult = null;
+ if (isActiveRequestForSingleAccessPoint()) {
+ approvedScanResult =
+ findUserApprovedAccessPointForActiveRequestFromActiveMatchedScanResults();
+ }
+ if (approvedScanResult != null
+ && !mWifiConfigManager.wasEphemeralNetworkDeleted(
+ ScanResultUtil.createQuotedSSID(approvedScanResult.SSID))) {
+ Log.v(TAG, "Approved access point found in matching scan results. "
+ + "Triggering connect " + approvedScanResult);
+ handleConnectToNetworkUserSelectionInternal(
+ ScanResultUtil.createNetworkFromScanResult(approvedScanResult));
+ mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass();
+ return true;
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "No approved access points found in matching scan results");
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the latest cached scan results from wifi scanner and filter out any
+ * {@link ScanResult} older than {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}.
+ */
+ private @NonNull ScanResult[] getFilteredCachedScanResults() {
+ List<ScanResult> cachedScanResults = mWifiScanner.getSingleScanResults();
+ if (cachedScanResults == null || cachedScanResults.isEmpty()) return new ScanResult[0];
+ long currentTimeInMillis = mClock.getElapsedSinceBootMillis();
+ return cachedScanResults.stream()
+ .filter(scanResult
+ -> ((currentTimeInMillis - (scanResult.timestamp / 1000))
+ < CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS))
+ .toArray(ScanResult[]::new);
+ }
+
+ /**
+ * Clean up least recently used Access Points if specified app reach the limit.
+ */
+ private static void cleanUpLRUAccessPoints(Set<AccessPoint> approvedAccessPoints) {
+ if (approvedAccessPoints.size() <= NUM_OF_ACCESS_POINT_LIMIT_PER_APP) {
+ return;
+ }
+ Iterator iter = approvedAccessPoints.iterator();
+ while (iter.hasNext() && approvedAccessPoints.size() > NUM_OF_ACCESS_POINT_LIMIT_PER_APP) {
+ iter.next();
+ iter.remove();
+ }
+ }
+
+ /**
* Remove all user approved access points for the specified app.
*/
public void removeUserApprovedAccessPointsForApp(@NonNull String packageName) {
- Iterator<Map.Entry<String, Set<AccessPoint>>> iter =
- mUserApprovedAccessPointMap.entrySet().iterator();
- while (iter.hasNext()) {
- Map.Entry<String, Set<AccessPoint>> entry = iter.next();
- if (packageName.equals(entry.getKey())) {
- Log.i(TAG, "Removing all approved access points for " + packageName);
- iter.remove();
- }
+ if (mUserApprovedAccessPointMap.remove(packageName) != null) {
+ Log.i(TAG, "Removing all approved access points for " + packageName);
}
saveToStore();
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
index 8feb371..426dddb 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
@@ -37,6 +37,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSuggestion;
+import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -51,6 +52,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -90,6 +92,10 @@
@VisibleForTesting
public static final String EXTRA_UID =
"com.android.server.wifi.extra.NetworkSuggestion.UID";
+ /**
+ * Limit number of hidden networks attach to scan
+ */
+ private static final int NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN = 100;
private final Context mContext;
private final Resources mResources;
@@ -571,6 +577,8 @@
if (mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) {
Log.i(TAG, "Setting the carrier provisioning app approved");
perAppInfo.hasUserApproved = true;
+ } else {
+ sendUserApprovalNotification(packageName, uid);
}
}
Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions =
@@ -640,7 +648,6 @@
perAppInfo.extNetworkSuggestions.removeAll(extNetworkSuggestions);
} else {
// empty list is used to clear everything for the app. Store a copy for use below.
- extNetworkSuggestions = new HashSet<>(perAppInfo.extNetworkSuggestions);
perAppInfo.extNetworkSuggestions.clear();
}
if (perAppInfo.extNetworkSuggestions.isEmpty()) {
@@ -667,7 +674,7 @@
* Remove the provided list of network suggestions from the corresponding app's active list.
*/
public @WifiManager.NetworkSuggestionsStatusCode int remove(
- List<WifiNetworkSuggestion> networkSuggestions, String packageName) {
+ List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Removing " + networkSuggestions.size() + " networks from " + packageName);
}
@@ -686,6 +693,13 @@
+ ". Network suggestions not found in active network suggestions");
return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID;
}
+ if (mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) {
+ // empty list is used to clear everything for the app.
+ if (extNetworkSuggestions.isEmpty()) {
+ extNetworkSuggestions = new HashSet<>(perAppInfo.extNetworkSuggestions);
+ }
+ triggerDisconnectIfServingNetworkSuggestionRemoved(extNetworkSuggestions);
+ }
removeInternal(extNetworkSuggestions, packageName, perAppInfo);
saveToStore();
mWifiMetrics.incrementNetworkSuggestionApiNumModification();
@@ -779,10 +793,11 @@
PendingIntent.FLAG_UPDATE_CURRENT);
}
- private @NonNull CharSequence getAppName(@NonNull String packageName) {
+ private @NonNull CharSequence getAppName(@NonNull String packageName, int uid) {
ApplicationInfo applicationInfo = null;
try {
- applicationInfo = mPackageManager.getApplicationInfo(packageName, 0);
+ applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0, UserHandle.getUserId(uid));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to find app name for " + packageName);
return "";
@@ -805,13 +820,14 @@
packageName, uid))
.build();
- CharSequence appName = getAppName(packageName);
+ CharSequence appName = getAppName(packageName, uid);
Notification notification = new Notification.Builder(
mContext, SystemNotificationChannels.NETWORK_STATUS)
.setSmallIcon(R.drawable.stat_notify_wifi_in_range)
.setTicker(mResources.getString(R.string.wifi_suggestion_title))
.setContentTitle(mResources.getString(R.string.wifi_suggestion_title))
- .setContentText(mResources.getString(R.string.wifi_suggestion_content, appName))
+ .setStyle(new Notification.BigTextStyle()
+ .bigText(mResources.getString(R.string.wifi_suggestion_content, appName)))
.setDeleteIntent(getPrivateBroadcast(NOTIFICATION_USER_DISMISSED_INTENT_ACTION,
packageName, uid))
.setShowWhen(false)
@@ -948,6 +964,28 @@
}
/**
+ * Get hidden network from active network suggestions.
+ * Todo(): Now limit by a fixed number, maybe we can try rotation?
+ * @return set of WifiConfigurations
+ */
+ public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks = new ArrayList<>();
+ for (PerAppInfo appInfo : mActiveNetworkSuggestionsPerApp.values()) {
+ if (!appInfo.hasUserApproved) continue;
+ for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions) {
+ if (!ewns.wns.wifiConfiguration.hiddenSSID) continue;
+ hiddenNetworks.add(
+ new WifiScanner.ScanSettings.HiddenNetwork(
+ ewns.wns.wifiConfiguration.SSID));
+ if (hiddenNetworks.size() >= NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN) {
+ return hiddenNetworks;
+ }
+ }
+ }
+ return hiddenNetworks;
+ }
+
+ /**
* Helper method to send the post connection broadcast to specified package.
*/
private void sendPostConnectionBroadcast(
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index 70a749b..33cc150 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -249,7 +249,8 @@
double filteredRssi = mVelocityBasedConnectedScore.getFilteredRssi();
double rssiThreshold = mVelocityBasedConnectedScore.getAdjustedRssiThreshold();
int freq = wifiInfo.getFrequency();
- int linkSpeed = wifiInfo.getLinkSpeed();
+ int txLinkSpeed = wifiInfo.getLinkSpeed();
+ int rxLinkSpeed = wifiInfo.getRxLinkSpeedMbps();
double txSuccessRate = wifiInfo.txSuccessRate;
double txRetriesRate = wifiInfo.txRetriesRate;
double txBadRate = wifiInfo.txBadRate;
@@ -258,9 +259,9 @@
try {
String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now));
s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
- "%s,%d,%d,%.1f,%.1f,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d,%d",
+ "%s,%d,%d,%.1f,%.1f,%.1f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d,%d",
timestamp, mSessionNumber, netId,
- rssi, filteredRssi, rssiThreshold, freq, linkSpeed,
+ rssi, filteredRssi, rssiThreshold, freq, txLinkSpeed, rxLinkSpeed,
txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate,
mNudYes, mNudCount,
s1, s2, score);
@@ -292,8 +293,8 @@
synchronized (mLinkMetricsHistory) {
history = new LinkedList<>(mLinkMetricsHistory);
}
- pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold,"
- + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,s1,s2,score");
+ pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold, freq,txLinkSpeed,"
+ + "rxLinkSpeed,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,s1,s2,score");
for (String line : history) {
pw.println(line);
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 6bb36b3..0afa317 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -528,10 +528,6 @@
if (mSettingsStore.handleAirplaneModeToggled()) {
mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED);
}
- if (mSettingsStore.isAirplaneModeOn()) {
- Log.d(TAG, "resetting country code because Airplane mode is ON");
- mCountryCode.airplaneModeEnabled();
- }
}
},
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
@@ -755,10 +751,11 @@
}
// Helper method to check if the entity initiating the binder call is a system app.
- private boolean isSystem(String packageName) {
+ private boolean isSystem(String packageName, int uid) {
long ident = Binder.clearCallingIdentity();
try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0, UserHandle.getUserId(uid));
return info.isSystemApp() || info.isUpdatedSystemApp();
} catch (PackageManager.NameNotFoundException e) {
// In case of exception, assume unknown app (more strict checking)
@@ -853,12 +850,12 @@
* Note: Invoke mAppOps.checkPackage(uid, packageName) before to ensure correct package name.
*/
private boolean isTargetSdkLessThanQOrPrivileged(String packageName, int pid, int uid) {
- return mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q)
+ return mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q, uid)
|| isPrivileged(pid, uid)
// DO/PO apps should be able to add/modify saved networks.
|| isDeviceOrProfileOwner(uid)
// TODO: Remove this system app bypass once Q is released.
- || isSystem(packageName)
+ || isSystem(packageName, uid)
|| mWifiPermissionsUtil.checkSystemAlertWindowPermission(uid, packageName);
}
@@ -874,8 +871,10 @@
return false;
}
boolean isPrivileged = isPrivileged(Binder.getCallingPid(), Binder.getCallingUid());
- if (!isPrivileged
- && !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q)) {
+ if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid())
+ && !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q,
+ Binder.getCallingUid())
+ && !isSystem(packageName, Binder.getCallingUid())) {
mLog.info("setWifiEnabled not allowed for uid=%")
.c(Binder.getCallingUid()).flush();
return false;
@@ -893,6 +892,11 @@
return false;
}
+ // If we're in crypt debounce, ignore any wifi state change APIs.
+ if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
+ return false;
+ }
+
mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName)
.c(Binder.getCallingUid()).c(enable).flush();
long ident = Binder.clearCallingIdentity();
@@ -1045,6 +1049,10 @@
public boolean startSoftAp(WifiConfiguration wifiConfig) {
// NETWORK_STACK is a signature only permission.
enforceNetworkStackPermission();
+ // If we're in crypt debounce, ignore any wifi state change APIs.
+ if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
+ return false;
+ }
mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush();
@@ -1091,6 +1099,10 @@
public boolean stopSoftAp() {
// NETWORK_STACK is a signature only permission.
enforceNetworkStackPermission();
+ // If we're in crypt debounce, ignore any wifi state change APIs.
+ if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
+ return false;
+ }
// only permitted callers are allowed to this point - they must have gone through
// connectivity service since this method is protected with the NETWORK_STACK PERMISSION
@@ -1421,6 +1433,10 @@
return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
}
+ if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
+ return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+ }
+
mLog.info("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush();
synchronized (mLocalOnlyHotspotRequests) {
@@ -2289,18 +2305,16 @@
@Override
public boolean removePasspointConfiguration(String fqdn, String packageName) {
final int uid = Binder.getCallingUid();
- if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
- && !mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) {
- if (mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q)) {
- return false;
- }
- throw new SecurityException(TAG + ": Permission denied");
+ boolean privileged = false;
+ if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
+ || mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) {
+ privileged = true;
}
mLog.info("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush();
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) {
return false;
}
- return mClientModeImpl.syncRemovePasspointConfig(mClientModeImplChannel, fqdn);
+ return mClientModeImpl.syncRemovePasspointConfig(mClientModeImplChannel, privileged, fqdn);
}
/**
@@ -2313,13 +2327,10 @@
@Override
public List<PasspointConfiguration> getPasspointConfigurations(String packageName) {
final int uid = Binder.getCallingUid();
- mAppOps.checkPackage(uid, packageName);
- if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
- && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)) {
- if (mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q)) {
- return new ArrayList<>();
- }
- throw new SecurityException(TAG + ": Permission denied");
+ boolean privileged = false;
+ if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
+ || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)) {
+ privileged = true;
}
if (mVerboseLoggingEnabled) {
mLog.info("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush();
@@ -2328,7 +2339,7 @@
PackageManager.FEATURE_WIFI_PASSPOINT)) {
return new ArrayList<>();
}
- return mClientModeImpl.syncGetPasspointConfigs(mClientModeImplChannel);
+ return mClientModeImpl.syncGetPasspointConfigs(mClientModeImplChannel, privileged);
}
/**
@@ -2812,6 +2823,10 @@
WorkSource updatedWs = (ws == null || ws.isEmpty())
? new WorkSource(Binder.getCallingUid()) : ws;
+ if (!WifiLockManager.isValidLockMode(lockMode)) {
+ throw new IllegalArgumentException("lockMode =" + lockMode);
+ }
+
Mutable<Boolean> lockSuccess = new Mutable<>();
boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors(
() -> {
@@ -2955,7 +2970,7 @@
if (mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_PASSPOINT)) {
List<PasspointConfiguration> configs = mClientModeImpl.syncGetPasspointConfigs(
- mClientModeImplChannel);
+ mClientModeImplChannel, true);
if (configs != null) {
for (PasspointConfiguration config : configs) {
removePasspointConfiguration(config.getHomeSp().getFqdn(), packageName);
@@ -2970,9 +2985,20 @@
mWifiNetworkSuggestionsManager.clear();
mWifiInjector.getWifiScoreCard().clear();
});
+ notifyFactoryReset();
}
}
+ /**
+ * Notify the Factory Reset Event to application who may installed wifi configurations.
+ */
+ private void notifyFactoryReset() {
+ Intent intent = new Intent(WifiManager.WIFI_NETWORK_SETTINGS_RESET_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.NETWORK_CARRIER_PROVISIONING);
+ }
+
/* private methods */
static boolean logAndReturnFalse(String s) {
Log.d(TAG, s);
@@ -3313,11 +3339,12 @@
if (mVerboseLoggingEnabled) {
mLog.info("removeNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush();
}
+ int callingUid = Binder.getCallingUid();
Mutable<Integer> success = new Mutable<>();
boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors(
() -> {
success.value = mWifiNetworkSuggestionsManager.remove(
- networkSuggestions, callingPackageName);
+ networkSuggestions, callingUid, callingPackageName);
}, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (!runWithScissorsSuccess) {
Log.e(TAG, "Failed to post runnable to remove network suggestions");
diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java
index 2565416..b2289a8 100644
--- a/service/java/com/android/server/wifi/WifiShellCommand.java
+++ b/service/java/com/android/server/wifi/WifiShellCommand.java
@@ -18,10 +18,14 @@
import android.app.AppGlobals;
import android.content.pm.IPackageManager;
+import android.net.wifi.WifiScanner;
import android.os.Binder;
import android.os.ShellCommand;
+import com.android.server.wifi.util.ApConfigUtil;
+
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -47,6 +51,9 @@
private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
private final WifiConfigManager mWifiConfigManager;
private final IPackageManager mPM;
+ private final WifiNative mWifiNative;
+ private final HostapdHal mHostapdHal;
+ private final WifiCountryCode mWifiCountryCode;
WifiShellCommand(WifiInjector wifiInjector) {
mClientModeImpl = wifiInjector.getClientModeImpl();
@@ -54,6 +61,9 @@
mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager();
mWifiConfigManager = wifiInjector.getWifiConfigManager();
mPM = AppGlobals.getPackageManager();
+ mHostapdHal = wifiInjector.getHostapdHal();
+ mWifiNative = wifiInjector.getWifiNative();
+ mWifiCountryCode = wifiInjector.getWifiCountryCode();
}
@Override
@@ -179,6 +189,62 @@
case "send-link-probe": {
return sendLinkProbe(pw);
}
+ case "force-softap-channel": {
+ String nextArg = getNextArgRequired();
+ if ("enabled".equals(nextArg)) {
+ int apChannelMHz;
+ try {
+ apChannelMHz = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ pw.println("Invalid argument to 'force-softap-channel enabled' "
+ + "- must be a positive integer");
+ return -1;
+ }
+ int apChannel = ApConfigUtil.convertFrequencyToChannel(apChannelMHz);
+ if (apChannel == -1 || !isApChannelMHzValid(apChannelMHz)) {
+ pw.println("Invalid argument to 'force-softap-channel enabled' "
+ + "- must be a valid WLAN channel");
+ return -1;
+ }
+ mHostapdHal.enableForceSoftApChannel(apChannel);
+ return 0;
+ } else if ("disabled".equals(nextArg)) {
+ mHostapdHal.disableForceSoftApChannel();
+ return 0;
+ } else {
+ pw.println(
+ "Invalid argument to 'force-softap-channel' - must be 'enabled'"
+ + " or 'disabled'");
+ return -1;
+ }
+ }
+ case "force-country-code": {
+ String nextArg = getNextArgRequired();
+ if ("enabled".equals(nextArg)) {
+ String countryCode = getNextArgRequired();
+ if (!(countryCode.length() == 2
+ && countryCode.chars().allMatch(Character::isLetter))) {
+ pw.println("Invalid argument to 'force-country-code enabled' "
+ + "- must be a two-letter string");
+ return -1;
+ }
+ mWifiCountryCode.enableForceCountryCode(countryCode);
+ return 0;
+ } else if ("disabled".equals(nextArg)) {
+ mWifiCountryCode.disableForceCountryCode();
+ return 0;
+ } else {
+ pw.println(
+ "Invalid argument to 'force-country-code' - must be 'enabled'"
+ + " or 'disabled'");
+ return -1;
+ }
+ }
+ case "get-country-code": {
+ pw.println("Wifi Country Code = "
+ + mWifiCountryCode.getCountryCode());
+ return 0;
+ }
default:
return handleDefaultCommands(cmd);
}
@@ -214,6 +280,25 @@
return 0;
}
+ private boolean isApChannelMHzValid(int apChannelMHz) {
+ int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
+ int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
+ int[] allowed5gDfsFreq =
+ mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
+ if (allowed2gFreq == null) {
+ allowed2gFreq = new int[0];
+ }
+ if (allowed5gFreq == null) {
+ allowed5gFreq = new int[0];
+ }
+ if (allowed5gDfsFreq == null) {
+ allowed5gDfsFreq = new int[0];
+ }
+ return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0
+ || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0
+ || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0);
+ }
+
private void checkRootPermission() {
final int uid = Binder.getCallingUid();
if (uid == 0) {
@@ -252,6 +337,13 @@
pw.println(" Clears the deleted ephemeral networks list.");
pw.println(" send-link-probe");
pw.println(" Manually triggers a link probe.");
+ pw.println(" force-softap-channel enabled <int> | disabled");
+ pw.println(" Sets whether soft AP channel is forced to <int> MHz");
+ pw.println(" or left for normal operation.");
+ pw.println(" force-country-code enabled <two-letter code> | disabled ");
+ pw.println(" Sets country code to <two-letter code> or left for normal value");
+ pw.println(" get-country-code");
+ pw.println(" Gets country code as a two-letter string");
pw.println();
}
}
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 8023bff..e780c10 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -1072,7 +1072,7 @@
/**
* Translation table used by getSupportedFeatureSet for translating IWifiChip caps for V1.1
*/
- private static final int[][] sChipFeatureCapabilityTranslation = {
+ private static final long[][] sChipFeatureCapabilityTranslation = {
{WifiManager.WIFI_FEATURE_TX_POWER_LIMIT,
android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT
},
@@ -1139,7 +1139,7 @@
/**
* Translation table used by getSupportedFeatureSet for translating IWifiStaIface caps
*/
- private static final int[][] sStaFeatureCapabilityTranslation = {
+ private static final long[][] sStaFeatureCapabilityTranslation = {
{WifiManager.WIFI_FEATURE_INFRA_5G,
IWifiStaIface.StaIfaceCapabilityMask.STA_5G
},
@@ -1188,8 +1188,8 @@
* @return bitmask defined by WifiManager.WIFI_FEATURE_*
*/
@VisibleForTesting
- int wifiFeatureMaskFromStaCapabilities(int capabilities) {
- int features = 0;
+ long wifiFeatureMaskFromStaCapabilities(int capabilities) {
+ long features = 0;
for (int i = 0; i < sStaFeatureCapabilityTranslation.length; i++) {
if ((capabilities & sStaFeatureCapabilityTranslation[i][1]) != 0) {
features |= sStaFeatureCapabilityTranslation[i][0];
@@ -2884,6 +2884,7 @@
mLog.e("Unexpected number of iface info in list " + numIfacesOnEachRadio);
return;
}
+ Runnable runnable = null;
// 2 ifaces simultaneous on 2 radios.
if (radioModeInfoList.size() == 2 && numIfacesOnEachRadio == 1) {
// Iface on radio0 should be different from the iface on radio1 for DBS & SBS.
@@ -2892,22 +2893,31 @@
return;
}
if (radioModeInfo0.bandInfo != radioModeInfo1.bandInfo) {
- handler.onDbs();
+ runnable = () -> {
+ handler.onDbs();
+ };
} else {
- handler.onSbs(radioModeInfo0.bandInfo);
+ runnable = () -> {
+ handler.onSbs(radioModeInfo0.bandInfo);
+ };
}
// 2 ifaces time sharing on 1 radio.
} else if (radioModeInfoList.size() == 1 && numIfacesOnEachRadio == 2) {
IfaceInfo ifaceInfo0 = radioModeInfo0.ifaceInfos.get(0);
IfaceInfo ifaceInfo1 = radioModeInfo0.ifaceInfos.get(1);
if (ifaceInfo0.channel != ifaceInfo1.channel) {
- handler.onMcc(radioModeInfo0.bandInfo);
+ runnable = () -> {
+ handler.onMcc(radioModeInfo0.bandInfo);
+ };
} else {
- handler.onScc(radioModeInfo0.bandInfo);
+ runnable = () -> {
+ handler.onScc(radioModeInfo0.bandInfo);
+ };
}
} else {
// Not concurrency scenario, uninteresting...
}
+ if (runnable != null) mHalEventHandler.post(runnable);
}
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index 1ac73ae..5832ee8 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -1286,7 +1286,7 @@
// Note: checks are done on the manager. This is a backup for apps which bypass the
// check.
if (!allowNdpResponderFromAnyOverride && !wifiPermissionsUtil.isTargetSdkLessThan(
- client.getCallingPackage(), Build.VERSION_CODES.P)) {
+ client.getCallingPackage(), Build.VERSION_CODES.P, uid)) {
if (ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB
&& ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB) {
Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
index d95ab38..69f98b0 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
@@ -75,10 +75,11 @@
*
* @param element The Roaming Consortium ANQP element
* @param providerOIs The roaming consortium OIs of the provider
+ * @param matchAll Indicates if a match with all OIs must be done
* @return true if a match is found
*/
public static boolean matchRoamingConsortium(RoamingConsortiumElement element,
- long[] providerOIs) {
+ long[] providerOIs, boolean matchAll) {
if (element == null) {
return false;
}
@@ -88,10 +89,14 @@
List<Long> rcOIs = element.getOIs();
for (long oi : providerOIs) {
if (rcOIs.contains(oi)) {
- return true;
+ if (!matchAll) {
+ return true;
+ }
+ } else if (matchAll) {
+ return false;
}
}
- return false;
+ return matchAll;
}
/**
@@ -100,27 +105,19 @@
*
* @param element The NAI Realm ANQP element
* @param realm The realm of the provider's credential
- * @param eapMethodID The EAP Method ID of the provider's credential
- * @param authParam The authentication parameter of the provider's credential
* @return an integer indicating the match status
*/
- public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID,
- AuthParam authParam) {
+ public static boolean matchNAIRealm(NAIRealmElement element, String realm) {
if (element == null || element.getRealmDataList().isEmpty()) {
- return AuthMatch.INDETERMINATE;
+ return false;
}
- int bestMatch = AuthMatch.NONE;
for (NAIRealmData realmData : element.getRealmDataList()) {
- int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam);
- if (match > bestMatch) {
- bestMatch = match;
- if (bestMatch == AuthMatch.EXACT) {
- break;
- }
+ if (matchNAIRealmData(realmData, realm)) {
+ return true;
}
}
- return bestMatch;
+ return false;
}
/**
@@ -172,42 +169,16 @@
*
* @param realmData The NAI Realm data
* @param realm The realm of the provider's credential
- * @param eapMethodID The EAP Method ID of the provider's credential
- * @param authParam The authentication parameter of the provider's credential
- * @return an integer indicating the match status
+ * @return true if a match is found
*/
- private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID,
- AuthParam authParam) {
+ private static boolean matchNAIRealmData(NAIRealmData realmData, String realm) {
// Check for realm domain name match.
- int realmMatch = AuthMatch.NONE;
for (String realmStr : realmData.getRealms()) {
if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) {
- realmMatch = AuthMatch.REALM;
- break;
+ return true;
}
}
-
- if (realmData.getEAPMethods().isEmpty()) {
- return realmMatch;
- }
-
- // Check for EAP method match.
- int eapMethodMatch = AuthMatch.NONE;
- for (EAPMethod eapMethod : realmData.getEAPMethods()) {
- eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam);
- if (eapMethodMatch != AuthMatch.NONE) {
- break;
- }
- }
-
- if (eapMethodMatch == AuthMatch.NONE) {
- return AuthMatch.NONE;
- }
-
- if (realmMatch == AuthMatch.NONE) {
- return eapMethodMatch;
- }
- return realmMatch | eapMethodMatch;
+ return false;
}
private static int getEapMethodForNAIRealmWithCarrier(String realm,
diff --git a/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java b/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java
index c748ca1..9256093 100644
--- a/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java
+++ b/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java
@@ -43,6 +43,7 @@
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -163,7 +164,7 @@
*/
public boolean connect(@NonNull URL url, @NonNull Network network) {
if (url == null) {
- Log.e(TAG, "url is null");
+ Log.e(TAG, "URL is null");
return false;
}
if (network == null) {
@@ -171,6 +172,14 @@
return false;
}
+ String protocol = url.getProtocol();
+ // According to section 7.5.1 OSU operational requirements, in HS2.0 R3 specification,
+ // the URL must be HTTPS. Enforce it here.
+ if (!TextUtils.equals(protocol, "https")) {
+ Log.e(TAG, "OSU server URL must be HTTPS");
+ return false;
+ }
+
mHandler.post(() -> performTlsConnection(url, network));
return true;
}
@@ -179,27 +188,25 @@
* Validates the service provider by comparing its identities found in OSU Server cert
* to the friendlyName obtained from ANQP exchange that is displayed to the user.
*
- * @param locale a {@link Locale} object used for matching the friendly name in
- * subjectAltName section of the certificate along with
- * {@param friendlyName}.
- * @param friendlyName a string of the friendly name used for finding the same name in
- * subjectAltName section of the certificate.
+ * @param friendlyNames the friendly names used for finding the same name in
+ * subjectAltName section of the certificate, which is a map of language
+ * codes from ISO-639 and names.
* @return boolean true if friendlyName shows up as one of the identities in the cert
*/
- public boolean validateProvider(Locale locale,
- String friendlyName) {
+ public boolean validateProvider(
+ Map<String, String> friendlyNames) {
- if (locale == null || TextUtils.isEmpty(friendlyName)) {
+ if (friendlyNames.size() == 0) {
return false;
}
for (Pair<Locale, String> identity : ServiceProviderVerifier.getProviderNames(
mTrustManager.getProviderCert())) {
- if (identity.first == null) continue;
+ if (identity.first == null || TextUtils.isEmpty(identity.second)) continue;
// Compare the language code for ISO-639.
- if (identity.first.getISO3Language().equals(locale.getISO3Language()) &&
- TextUtils.equals(identity.second, friendlyName)) {
+ if (TextUtils.equals(identity.second,
+ friendlyNames.get(identity.first.getISO3Language()))) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "OSU certificate is valid for "
+ identity.first.getISO3Language() + "/" + identity.second);
@@ -271,13 +278,37 @@
mNetwork = network;
mUrl = url;
- HttpsURLConnection urlConnection;
+ URLConnection urlConnection;
+ HttpsURLConnection httpsURLConnection;
+
try {
- urlConnection = (HttpsURLConnection) mNetwork.openConnection(mUrl);
- urlConnection.setSSLSocketFactory(mSocketFactory);
- urlConnection.setConnectTimeout(HttpsServiceConnection.DEFAULT_TIMEOUT_MS);
- urlConnection.setReadTimeout(HttpsServiceConnection.DEFAULT_TIMEOUT_MS);
- urlConnection.connect();
+ urlConnection = mNetwork.openConnection(mUrl);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to establish a URL connection: " + e);
+ if (mOsuServerCallbacks != null) {
+ mOsuServerCallbacks.onServerConnectionStatus(
+ mOsuServerCallbacks.getSessionId(),
+ false);
+ }
+ return;
+ }
+
+ if (urlConnection instanceof HttpsURLConnection) {
+ httpsURLConnection = (HttpsURLConnection) urlConnection;
+ } else {
+ Log.e(TAG, "Invalid URL connection");
+ if (mOsuServerCallbacks != null) {
+ mOsuServerCallbacks.onServerConnectionStatus(mOsuServerCallbacks.getSessionId(),
+ false);
+ }
+ return;
+ }
+
+ try {
+ httpsURLConnection.setSSLSocketFactory(mSocketFactory);
+ httpsURLConnection.setConnectTimeout(HttpsServiceConnection.DEFAULT_TIMEOUT_MS);
+ httpsURLConnection.setReadTimeout(HttpsServiceConnection.DEFAULT_TIMEOUT_MS);
+ httpsURLConnection.connect();
} catch (IOException e) {
Log.e(TAG, "Unable to establish a URL connection: " + e);
if (mOsuServerCallbacks != null) {
@@ -286,7 +317,7 @@
}
return;
}
- mUrlConnection = urlConnection;
+ mUrlConnection = httpsURLConnection;
if (mOsuServerCallbacks != null) {
mOsuServerCallbacks.onServerConnectionStatus(mOsuServerCallbacks.getSessionId(), true);
}
@@ -572,9 +603,15 @@
(SSLSocket) null);
certsValid = true;
} catch (CertificateException e) {
- Log.e(TAG, "Unable to validate certs " + e);
- if (mVerboseLoggingEnabled) {
- e.printStackTrace();
+ Log.e(TAG, "Certificate validation failure: " + e);
+ int i = 0;
+ for (X509Certificate cert : chain) {
+ // Provide some more details about the invalid certificate
+ Log.e(TAG, "Cert " + i + " details: " + cert.getSubjectDN());
+ Log.e(TAG, "Not before: " + cert.getNotBefore() + ", not after: "
+ + cert.getNotAfter());
+ Log.e(TAG, "Cert " + i + " issuer: " + cert.getIssuerDN());
+ i++;
}
}
if (mOsuServerCallbacks != null) {
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java
index 419ea79..7f5a6b4 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java
@@ -16,7 +16,10 @@
package com.android.server.wifi.hotspot2;
+import android.annotation.Nullable;
+
import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -72,13 +75,16 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
serializeShareData(out);
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java
index 0114cfb..123cf89 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java
@@ -16,6 +16,7 @@
package com.android.server.wifi.hotspot2;
+import android.annotation.Nullable;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.text.TextUtils;
@@ -23,6 +24,7 @@
import com.android.server.wifi.SIMAccessor;
import com.android.server.wifi.WifiConfigStore;
import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import org.xmlpull.v1.XmlPullParser;
@@ -103,13 +105,16 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
serializeUserData(out);
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth,
+ @WifiConfigStore.Version int version,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 8f4349a..4fce556 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -178,6 +178,7 @@
public void setProviders(List<PasspointProvider> providers) {
mProviders.clear();
for (PasspointProvider provider : providers) {
+ provider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0);
mProviders.put(provider.getConfig().getHomeSp().getFqdn(), provider);
if (provider.getPackageName() != null) {
startTrackingAppOpsChange(provider.getPackageName(),
@@ -249,7 +250,7 @@
for (Map.Entry<String, PasspointProvider> entry : getPasspointProviderWithPackage(
packageName).entrySet()) {
String fqdn = entry.getValue().getConfig().getHomeSp().getFqdn();
- removeProvider(fqdn);
+ removeProvider(Process.WIFI_UID /* ignored */, true, fqdn);
disconnectIfPasspointNetwork(fqdn);
}
}
@@ -274,7 +275,7 @@
AppOpsChangedListener appOpsChangedListener = mAppOpsChangedListenerPerApp.remove(
packageName);
if (appOpsChangedListener == null) {
- Log.wtf(TAG, "No app ops listener found for " + packageName);
+ Log.i(TAG, "No app ops listener found for " + packageName);
return;
}
mAppOps.stopWatchingMode(appOpsChangedListener);
@@ -338,6 +339,9 @@
public void enableVerboseLogging(int verbose) {
mVerboseLoggingEnabled = (verbose > 0) ? true : false;
mPasspointProvisioner.enableVerboseLogging(verbose);
+ for (PasspointProvider provider : mProviders.values()) {
+ provider.enableVerboseLogging(verbose);
+ }
}
/**
@@ -394,6 +398,7 @@
mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys();
mProviders.remove(config.getHomeSp().getFqdn());
}
+ newProvider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0);
mProviders.put(config.getHomeSp().getFqdn(), newProvider);
mWifiConfigManager.saveToStore(true /* forceWrite */);
if (newProvider.getPackageName() != null) {
@@ -619,18 +624,26 @@
/**
* Remove a Passpoint provider identified by the given FQDN.
*
+ * @param callingUid Calling UID.
+ * @param privileged Whether the caller is a privileged entity
* @param fqdn The FQDN of the provider to remove
* @return true if a provider is removed, false otherwise
*/
- public boolean removeProvider(String fqdn) {
+ public boolean removeProvider(int callingUid, boolean privileged, String fqdn) {
mWifiMetrics.incrementNumPasspointProviderUninstallation();
String packageName;
- if (!mProviders.containsKey(fqdn)) {
+ PasspointProvider provider = mProviders.get(fqdn);
+ if (provider == null) {
Log.e(TAG, "Config doesn't exist");
return false;
}
- mProviders.get(fqdn).uninstallCertsAndKeys();
- packageName = mProviders.get(fqdn).getPackageName();
+ if (!privileged && callingUid != provider.getCreatorUid()) {
+ Log.e(TAG, "UID " + callingUid + " cannot remove profile created by "
+ + provider.getCreatorUid());
+ return false;
+ }
+ provider.uninstallCertsAndKeys();
+ packageName = provider.getPackageName();
mProviders.remove(fqdn);
mWifiConfigManager.saveToStore(true /* forceWrite */);
@@ -663,12 +676,17 @@
*
* An empty list will be returned when no provider is installed.
*
+ * @param callingUid Calling UID.
+ * @param privileged Whether the caller is a privileged entity
* @return A list of {@link PasspointConfiguration}
*/
- public List<PasspointConfiguration> getProviderConfigs() {
+ public List<PasspointConfiguration> getProviderConfigs(int callingUid, boolean privileged) {
List<PasspointConfiguration> configs = new ArrayList<>();
for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
- configs.add(entry.getValue().getConfig());
+ PasspointProvider provider = entry.getValue();
+ if (privileged || callingUid == provider.getCreatorUid()) {
+ configs.add(provider.getConfig());
+ }
}
return configs;
}
@@ -968,7 +986,8 @@
public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
List<OsuProvider> osuProviders) {
Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigs = new HashMap<>();
- List<PasspointConfiguration> passpointConfigurations = getProviderConfigs();
+ List<PasspointConfiguration> passpointConfigurations =
+ getProviderConfigs(Process.WIFI_UID /* ignored */, true);
for (OsuProvider osuProvider : osuProviders) {
Map<String, String> friendlyNamesForOsuProvider = osuProvider.getFriendlyNameList();
@@ -1112,6 +1131,7 @@
Arrays.asList(enterpriseConfig.getCaCertificateAlias()),
enterpriseConfig.getClientCertificateAlias(),
enterpriseConfig.getClientCertificateAlias(), null, false, false);
+ provider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0);
mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider);
return true;
}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
index ec8a009..148af39 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
@@ -231,24 +231,20 @@
if (existingNetwork != null) {
WifiConfiguration.NetworkSelectionStatus status =
existingNetwork.getNetworkSelectionStatus();
- if (!status.isNetworkEnabled()) {
- boolean isSuccess = mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId);
- if (isSuccess) {
- return existingNetwork;
- }
+ if (!status.isNetworkEnabled()
+ && !mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId)) {
localLog("Current configuration for the Passpoint AP " + config.SSID
+ " is disabled, skip this candidate");
return null;
}
- return existingNetwork;
}
- // Add the newly created WifiConfiguration to WifiConfigManager.
+ // Add or update with the newly created WifiConfiguration to WifiConfigManager.
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
if (!result.isSuccess()) {
localLog("Failed to add passpoint network");
- return null;
+ return existingNetwork;
}
mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID);
mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(),
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
index ca9814a..8db71d3 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -95,6 +95,7 @@
private boolean mHasEverConnected;
private boolean mIsShared;
+ private boolean mVerboseLoggingEnabled;
/**
* This is a flag to indicate if the Provider is created temporarily.
@@ -327,39 +328,53 @@
* Return the matching status with the given AP, based on the ANQP elements from the AP.
*
* @param anqpElements ANQP elements from the AP
- * @param roamingConsortium Roaming Consortium information element from the AP
+ * @param roamingConsortiumFromAp Roaming Consortium information element from the AP
* @return {@link PasspointMatch}
*/
public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements,
- RoamingConsortium roamingConsortium) {
- PasspointMatch providerMatch = matchProviderExceptFor3GPP(anqpElements, roamingConsortium);
+ RoamingConsortium roamingConsortiumFromAp) {
+ // Match FQDN for Home provider or RCOI(s) for Roaming provider
+ // For SIM credential, the FQDN is in the format of wlan.mnc*.mcc*.3gppnetwork.org
+ PasspointMatch providerMatch = matchFqdnAndRcoi(anqpElements, roamingConsortiumFromAp);
// 3GPP Network matching.
if (providerMatch == PasspointMatch.None && ANQPMatcher.matchThreeGPPNetwork(
(ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork),
mImsiParameter, mMatchingSIMImsiList)) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Final RoamingProvider match with "
+ + anqpElements.get(ANQPElementType.ANQP3GPPNetwork));
+ }
return PasspointMatch.RoamingProvider;
}
- // Perform authentication match against the NAI Realm.
- int authMatch = ANQPMatcher.matchNAIRealm(
+ // Perform NAI Realm matching
+ boolean realmMatch = ANQPMatcher.matchNAIRealm(
(NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm),
- mConfig.getCredential().getRealm(), mEAPMethodID, mAuthParam);
-
- // In case of Auth mismatch, demote provider match.
- if (authMatch == AuthMatch.NONE) {
- return PasspointMatch.None;
- }
+ mConfig.getCredential().getRealm());
// In case of no realm match, return provider match as is.
- if ((authMatch & AuthMatch.REALM) == 0) {
+ if (!realmMatch) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "No NAI realm match, final match: " + providerMatch);
+ }
return providerMatch;
}
- // Promote the provider match to roaming provider if provider match is not found, but NAI
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "NAI realm match with " + mConfig.getCredential().getRealm());
+ }
+
+ // Promote the provider match to RoamingProvider if provider match is not found, but NAI
// realm is matched.
- return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider
- : providerMatch;
+ if (providerMatch == PasspointMatch.None) {
+ providerMatch = PasspointMatch.RoamingProvider;
+ }
+
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Final match: " + providerMatch);
+ }
+ return providerMatch;
}
/**
@@ -570,42 +585,129 @@
}
/**
+ * Match given OIs to the Roaming Consortium OIs
+ *
+ * @param providerOis Provider OIs to match against
+ * @param roamingConsortiumElement RCOIs in the ANQP element
+ * @param roamingConsortiumFromAp RCOIs in the AP scan results
+ * @param matchAll Indicates if all providerOis must match the RCOIs elements
+ * @return {@code true} if there is a match, {@code false} otherwise.
+ */
+ private boolean matchOis(long[] providerOis,
+ RoamingConsortiumElement roamingConsortiumElement,
+ RoamingConsortium roamingConsortiumFromAp,
+ boolean matchAll) {
+
+
+ // ANQP Roaming Consortium OI matching.
+ if (ANQPMatcher.matchRoamingConsortium(roamingConsortiumElement, providerOis, matchAll)) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "ANQP RCOI match " + roamingConsortiumElement);
+ }
+ return true;
+ }
+
+ // AP Roaming Consortium OI matching.
+ long[] apRoamingConsortiums = roamingConsortiumFromAp.getRoamingConsortiums();
+ if (apRoamingConsortiums == null || providerOis == null) {
+ return false;
+ }
+ // Roaming Consortium OI information element matching.
+ for (long apOi: apRoamingConsortiums) {
+ boolean matched = false;
+ for (long providerOi: providerOis) {
+ if (apOi == providerOi) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "AP RCOI match: " + apOi);
+ }
+ if (!matchAll) {
+ return true;
+ } else {
+ matched = true;
+ break;
+ }
+ }
+ }
+ if (matchAll && !matched) {
+ return false;
+ }
+ }
+ return matchAll;
+ }
+
+ /**
* Perform a provider match based on the given ANQP elements except for matching 3GPP Network.
*
* @param anqpElements List of ANQP elements
- * @param roamingConsortium Roaming Consortium information element from the AP
+ * @param roamingConsortiumFromAp Roaming Consortium information element from the AP
* @return {@link PasspointMatch}
*/
- private PasspointMatch matchProviderExceptFor3GPP(
+ private PasspointMatch matchFqdnAndRcoi(
Map<ANQPElementType, ANQPElement> anqpElements,
- RoamingConsortium roamingConsortium) {
+ RoamingConsortium roamingConsortiumFromAp) {
// Domain name matching.
if (ANQPMatcher.matchDomainName(
(DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName),
mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Domain name " + mConfig.getHomeSp().getFqdn()
+ + " match: HomeProvider");
+ }
return PasspointMatch.HomeProvider;
}
- // ANQP Roaming Consortium OI matching.
- long[] providerOIs = mConfig.getHomeSp().getRoamingConsortiumOis();
- if (ANQPMatcher.matchRoamingConsortium(
- (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
- providerOIs)) {
- return PasspointMatch.RoamingProvider;
- }
-
- long[] roamingConsortiums = roamingConsortium.getRoamingConsortiums();
- // Roaming Consortium OI information element matching.
- if (roamingConsortiums != null && providerOIs != null) {
- for (long sta_oi: roamingConsortiums) {
- for (long ap_oi: providerOIs) {
- if (sta_oi == ap_oi) {
- return PasspointMatch.RoamingProvider;
+ // Other Home Partners matching.
+ if (mConfig.getHomeSp().getOtherHomePartners() != null) {
+ for (String otherHomePartner : mConfig.getHomeSp().getOtherHomePartners()) {
+ if (ANQPMatcher.matchDomainName(
+ (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName),
+ otherHomePartner, null, null)) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Other Home Partner " + otherHomePartner
+ + " match: HomeProvider");
}
+ return PasspointMatch.HomeProvider;
}
}
}
+ // HomeOI matching
+ if (mConfig.getHomeSp().getMatchAllOis() != null) {
+ // Ensure that every HomeOI whose corresponding HomeOIRequired value is true shall match
+ // an OI in the Roaming Consortium advertised by the hotspot operator.
+ if (matchOis(mConfig.getHomeSp().getMatchAllOis(), (RoamingConsortiumElement)
+ anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
+ roamingConsortiumFromAp, true)) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "All HomeOI RCOI match: HomeProvider");
+ }
+ return PasspointMatch.HomeProvider;
+ }
+ } else if (mConfig.getHomeSp().getMatchAnyOis() != null) {
+ // Ensure that any HomeOI whose corresponding HomeOIRequired value is false shall match
+ // an OI in the Roaming Consortium advertised by the hotspot operator.
+ if (matchOis(mConfig.getHomeSp().getMatchAnyOis(), (RoamingConsortiumElement)
+ anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
+ roamingConsortiumFromAp, false)) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "Any HomeOI RCOI match: HomeProvider");
+ }
+ return PasspointMatch.HomeProvider;
+ }
+ }
+
+ // Roaming Consortium OI matching.
+ if (matchOis(mConfig.getHomeSp().getRoamingConsortiumOis(), (RoamingConsortiumElement)
+ anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
+ roamingConsortiumFromAp, false)) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "ANQP RCOI match: RoamingProvider");
+ }
+ return PasspointMatch.RoamingProvider;
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "No domain name or RCOI match");
+ }
return PasspointMatch.None;
}
@@ -768,4 +870,12 @@
simCredential.setEapType(eapType);
return simCredential;
}
+
+ /**
+ * Enable verbose logging
+ * @param verbose more than 0 enables verbose logging
+ */
+ public void enableVerboseLogging(int verbose) {
+ mVerboseLoggingEnabled = (verbose > 0) ? true : false;
+ }
}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
index bdd035f..137d9fa 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
@@ -366,7 +366,7 @@
return;
}
if (!mOsuServerConnection.validateProvider(
- Locale.getDefault(), mOsuProvider.getFriendlyName())) {
+ mOsuProvider.getFriendlyNameList())) {
Log.e(TAG,
"OSU Server certificate does not have the one matched with the selected "
+ "Service Name: "
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index 3f37456..055fa72 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -100,6 +100,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -148,6 +149,11 @@
android.Manifest.permission.ACCESS_WIFI_STATE
};
+ // Maximum number of bytes allowed for a network name, i.e. SSID.
+ private static final int MAX_NETWORK_NAME_BYTES = 32;
+ // Minimum number of bytes for a network name, i.e. DIRECT-xy.
+ private static final int MIN_NETWORK_NAME_BYTES = 9;
+
// Two minutes comes from the wpa_supplicant setting
private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
private static int sGroupCreatingTimeoutIndex = 0;
@@ -3255,6 +3261,23 @@
}
/**
+ * Check the network name complies standard SSID naming rules.
+ *
+ * The network name of a group is also the broadcasting SSID,
+ * as a result, the network name must complies standard SSID naming
+ * rules.
+ */
+ private boolean isValidNetworkName(String networkName) {
+ if (TextUtils.isEmpty(networkName)) return false;
+
+ byte[] ssidBytes = networkName.getBytes(StandardCharsets.UTF_8);
+ if (ssidBytes.length < MIN_NETWORK_NAME_BYTES) return false;
+ if (ssidBytes.length > MAX_NETWORK_NAME_BYTES) return false;
+
+ return true;
+ }
+
+ /**
* A config is valid as a group if it has network name and passphrase.
* Supplicant can construct a group on the fly for creating a group with specified config
* or join a group without negotiation and WPS.
@@ -3264,7 +3287,7 @@
private boolean isConfigValidAsGroup(WifiP2pConfig config) {
if (config == null) return false;
if (TextUtils.isEmpty(config.deviceAddress)) return false;
- if (!TextUtils.isEmpty(config.networkName)
+ if (isValidNetworkName(config.networkName)
&& !TextUtils.isEmpty(config.passphrase)) {
return true;
}
diff --git a/service/java/com/android/server/wifi/rtt/RttNative.java b/service/java/com/android/server/wifi/rtt/RttNative.java
index eaf9470..ffbf5be 100644
--- a/service/java/com/android/server/wifi/rtt/RttNative.java
+++ b/service/java/com/android/server/wifi/rtt/RttNative.java
@@ -311,6 +311,7 @@
config.channel.centerFreq1 = responder.centerFreq1;
config.bw = halRttChannelBandwidthFromResponderChannelWidth(responder.channelWidth);
config.preamble = halRttPreambleFromResponderPreamble(responder.preamble);
+ validateBwAndPreambleCombination(config.bw, config.preamble);
if (config.peer == RttPeerType.NAN) {
config.mustRequestLci = false;
@@ -349,6 +350,20 @@
return rttConfigs;
}
+ private static void validateBwAndPreambleCombination(int bw, int preamble) {
+ if (bw <= RttBw.BW_20MHZ) {
+ return;
+ }
+ if (bw == RttBw.BW_40MHZ && preamble >= RttPreamble.HT) {
+ return;
+ }
+ if (bw >= RttBw.BW_80MHZ && preamble >= RttPreamble.VHT) {
+ return;
+ }
+ throw new IllegalArgumentException(
+ "bw and preamble combination is invalid, bw: " + bw + " preamble: " + preamble);
+ }
+
private static int halRttPeerTypeFromResponderType(int responderType) {
switch (responderType) {
case ResponderConfig.RESPONDER_AP:
diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
index 19ae154..d69ce8f 100644
--- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
+++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -1178,16 +1178,11 @@
"ResponderLocation: lci/lcr parser failed exception -- " + e);
}
// Clear LCI and LCR data if the location data should not be retransmitted,
- // has a retention expiration time, contains no useful data, or did not parse.
- if (responderLocation == null) {
+ // has a retention expiration time, contains no useful data, or did not parse,
+ // or the caller is not in a privileged context.
+ if (responderLocation == null || !isCalledFromPrivilegedContext) {
lci = null;
lcr = null;
- } else if (!isCalledFromPrivilegedContext) {
- // clear the raw lci and lcr buffers and civic location data if the
- // caller is not in a privileged context.
- lci = null;
- lcr = null;
- responderLocation.setCivicLocationSubelementDefaults();
}
if (resultForRequest.successNumber <= 1
&& resultForRequest.distanceSdInMm != 0) {
diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java
index dfda45b..c3a4c0c 100644
--- a/service/java/com/android/server/wifi/util/ApConfigUtil.java
+++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java
@@ -33,7 +33,7 @@
public static final int DEFAULT_AP_BAND = WifiConfiguration.AP_BAND_2GHZ;
public static final int DEFAULT_AP_CHANNEL = 6;
-
+ public static final int HIGHEST_2G_AP_CHANNEL = 14;
/* Return code for updateConfiguration. */
public static final int SUCCESS = 0;
public static final int ERROR_NO_CHANNEL = 1;
diff --git a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java b/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
deleted file mode 100644
index 1f3c6b3..0000000
--- a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2019 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.util;
-
-import android.annotation.NonNull;
-import android.os.SystemProperties;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.security.DigestException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.UnrecoverableEntryException;
-import java.security.cert.CertificateException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-/**
- * Tools to provide integrity checking of byte arrays based on NIAP Common Criteria Protection
- * Profile <a href="https://www.niap-ccevs.org/MMO/PP/-417-/#FCS_STG_EXT.3.1">FCS_STG_EXT.3.1</a>.
- */
-public class DataIntegrityChecker {
- private static final String TAG = "DataIntegrityChecker";
-
- private static final String FILE_SUFFIX = ".encrypted-checksum";
- private static final String ALIAS_SUFFIX = ".data-integrity-checker-key";
- private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
- private static final String DIGEST_ALGORITHM = "SHA-256";
- private static final int GCM_TAG_LENGTH = 128;
- private static final String KEY_STORE = "AndroidKeyStore";
-
- /**
- * When KEYSTORE_FAILURE_RETURN_VALUE is true, all cryptographic operation failures will not
- * enforce security and {@link #isOk(byte[])} always return true.
- */
- private static final boolean KEYSTORE_FAILURE_RETURN_VALUE = true;
-
- private final File mIntegrityFile;
-
- /**
- * Construct a new integrity checker to update and check if/when a data file was altered
- * outside expected conditions.
- *
- * @param integrityFilename The {@link File} path prefix for where the integrity data is stored.
- * A file will be created in the name of integrityFile with the suffix
- * {@link DataIntegrityChecker#FILE_SUFFIX} We recommend using the same
- * path as the file for which integrity is performed on.
- * @throws NullPointerException When integrity file is null or the empty string.
- */
- public DataIntegrityChecker(@NonNull String integrityFilename) {
- if (TextUtils.isEmpty(integrityFilename)) {
- throw new NullPointerException("integrityFilename must not be null or the empty "
- + "string");
- }
- mIntegrityFile = new File(integrityFilename + FILE_SUFFIX);
- }
-
- /**
- * Computes a digest of a byte array, encrypt it, and store the result
- *
- * Call this method immediately before storing the byte array
- *
- * @param data The data desired to ensure integrity
- */
- public void update(byte[] data) {
- if (data == null || data.length < 1) {
- reportException(new Exception("No data to update"), "No data to update.");
- return;
- }
- byte[] digest = getDigest(data);
- if (digest == null || digest.length < 1) {
- return;
- }
- String alias = mIntegrityFile.getName() + ALIAS_SUFFIX;
- EncryptedData integrityData = encrypt(digest, alias);
- if (integrityData != null) {
- writeIntegrityData(integrityData, mIntegrityFile);
- } else {
- reportException(new Exception("integrityData null upon update"),
- "integrityData null upon update");
- }
- }
-
- /**
- * Check the integrity of a given byte array
- *
- * Call this method immediately before trusting the byte array. This method will return false
- * when the byte array was altered since the last {@link #update(byte[])}
- * call, when {@link #update(byte[])} has never been called, or if there is
- * an underlying issue with the cryptographic functions or the key store.
- *
- * @param data The data to check if its been altered
- * @throws DigestException The integrity mIntegrityFile cannot be read. Ensure
- * {@link #isOk(byte[])} is called after {@link #update(byte[])}. Otherwise, consider the
- * result vacuously true and immediately call {@link #update(byte[])}.
- * @return true if the data was not altered since {@link #update(byte[])} was last called
- */
- public boolean isOk(byte[] data) throws DigestException {
- if (data == null || data.length < 1) {
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
- byte[] currentDigest = getDigest(data);
- if (currentDigest == null || currentDigest.length < 1) {
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
-
- EncryptedData encryptedData = null;
-
- try {
- encryptedData = readIntegrityData(mIntegrityFile);
- } catch (IOException e) {
- reportException(e, "readIntegrityData had an IO exception");
- return KEYSTORE_FAILURE_RETURN_VALUE;
- } catch (ClassNotFoundException e) {
- reportException(e, "readIntegrityData could not find the class EncryptedData");
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
-
- if (encryptedData == null) {
- // File not found is not considered to be an error.
- throw new DigestException("No stored digest is available to compare.");
- }
- byte[] storedDigest = decrypt(encryptedData);
- if (storedDigest == null) {
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
- return constantTimeEquals(storedDigest, currentDigest);
- }
-
- private byte[] getDigest(byte[] data) {
- try {
- return MessageDigest.getInstance(DIGEST_ALGORITHM).digest(data);
- } catch (NoSuchAlgorithmException e) {
- reportException(e, "getDigest could not find algorithm: " + DIGEST_ALGORITHM);
- return null;
- }
- }
-
- private EncryptedData encrypt(byte[] data, String keyAlias) {
- EncryptedData encryptedData = null;
- try {
- Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- SecretKey secretKeyReference = getOrCreateSecretKey(keyAlias);
- if (secretKeyReference != null) {
- cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
- encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV(), keyAlias);
- } else {
- reportException(new Exception("secretKeyReference is null."),
- "secretKeyReference is null.");
- }
- } catch (NoSuchAlgorithmException e) {
- reportException(e, "encrypt could not find the algorithm: " + CIPHER_ALGORITHM);
- } catch (NoSuchPaddingException e) {
- reportException(e, "encrypt had a padding exception");
- } catch (InvalidKeyException e) {
- reportException(e, "encrypt received an invalid key");
- } catch (BadPaddingException e) {
- reportException(e, "encrypt had a padding problem");
- } catch (IllegalBlockSizeException e) {
- reportException(e, "encrypt had an illegal block size");
- }
- return encryptedData;
- }
-
- private byte[] decrypt(EncryptedData encryptedData) {
- byte[] decryptedData = null;
- try {
- Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, encryptedData.getIv());
- SecretKey secretKeyReference = getOrCreateSecretKey(encryptedData.getKeyAlias());
- if (secretKeyReference != null) {
- cipher.init(Cipher.DECRYPT_MODE, secretKeyReference, spec);
- decryptedData = cipher.doFinal(encryptedData.getEncryptedData());
- }
- } catch (NoSuchAlgorithmException e) {
- reportException(e, "decrypt could not find cipher algorithm " + CIPHER_ALGORITHM);
- } catch (NoSuchPaddingException e) {
- reportException(e, "decrypt could not find padding algorithm");
- } catch (IllegalBlockSizeException e) {
- reportException(e, "decrypt had a illegal block size");
- } catch (BadPaddingException e) {
- reportException(e, "decrypt had bad padding");
- } catch (InvalidKeyException e) {
- reportException(e, "decrypt had an invalid key");
- } catch (InvalidAlgorithmParameterException e) {
- reportException(e, "decrypt had an invalid algorithm parameter");
- }
- return decryptedData;
- }
-
- private SecretKey getOrCreateSecretKey(String keyAlias) {
- SecretKey secretKey = null;
- try {
- KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
- keyStore.load(null);
- if (keyStore.containsAlias(keyAlias)) { // The key exists in key store. Get the key.
- KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
- .getEntry(keyAlias, null);
- if (secretKeyEntry != null) {
- secretKey = secretKeyEntry.getSecretKey();
- } else {
- reportException(new Exception("keystore contains the alias and the secret key "
- + "entry was null"),
- "keystore contains the alias and the secret key entry was null");
- }
- } else { // The key does not exist in key store. Create the key and store it.
- KeyGenerator keyGenerator = KeyGenerator
- .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
-
- KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias,
- KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build();
-
- keyGenerator.init(keyGenParameterSpec);
- secretKey = keyGenerator.generateKey();
- }
- } catch (CertificateException e) {
- reportException(e, "getOrCreateSecretKey had a certificate exception.");
- } catch (InvalidAlgorithmParameterException e) {
- reportException(e, "getOrCreateSecretKey had an invalid algorithm parameter");
- } catch (IOException e) {
- reportException(e, "getOrCreateSecretKey had an IO exception.");
- } catch (KeyStoreException e) {
- reportException(e, "getOrCreateSecretKey cannot find the keystore: " + KEY_STORE);
- } catch (NoSuchAlgorithmException e) {
- reportException(e, "getOrCreateSecretKey cannot find algorithm");
- } catch (NoSuchProviderException e) {
- reportException(e, "getOrCreateSecretKey cannot find crypto provider");
- } catch (UnrecoverableEntryException e) {
- reportException(e, "getOrCreateSecretKey had an unrecoverable entry exception.");
- }
- return secretKey;
- }
-
- private void writeIntegrityData(EncryptedData encryptedData, File file) {
- try (FileOutputStream fos = new FileOutputStream(file);
- ObjectOutputStream oos = new ObjectOutputStream(fos)) {
- oos.writeObject(encryptedData);
- } catch (FileNotFoundException e) {
- reportException(e, "writeIntegrityData could not find the integrity file");
- } catch (IOException e) {
- reportException(e, "writeIntegrityData had an IO exception");
- }
- }
-
- private EncryptedData readIntegrityData(File file) throws IOException, ClassNotFoundException {
- try (FileInputStream fis = new FileInputStream(file);
- ObjectInputStream ois = new ObjectInputStream(fis)) {
- return (EncryptedData) ois.readObject();
- } catch (FileNotFoundException e) {
- // File not found, this is not considered to be a real error. The file will be created
- // by the system next time the data file is written. Note that it is not possible for
- // non system user to delete or modify the file.
- Log.w(TAG, "readIntegrityData could not find integrity file");
- }
- return null;
- }
-
- private boolean constantTimeEquals(byte[] a, byte[] b) {
- if (a == null && b == null) {
- return true;
- }
-
- if (a == null || b == null || a.length != b.length) {
- return false;
- }
-
- byte differenceAccumulator = 0;
- for (int i = 0; i < a.length; ++i) {
- differenceAccumulator |= a[i] ^ b[i];
- }
- return (differenceAccumulator == 0);
- }
-
- /* TODO(b/128526030): Remove this error reporting code upon resolving the bug. */
- private static final boolean REQUEST_BUG_REPORT = false;
- private void reportException(Exception exception, String error) {
- Log.wtf(TAG, "An irrecoverable key store error was encountered: " + error);
- if (REQUEST_BUG_REPORT) {
- SystemProperties.set("dumpstate.options", "bugreportwifi");
- SystemProperties.set("ctl.start", "bugreport");
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/util/EncryptedData.java b/service/java/com/android/server/wifi/util/EncryptedData.java
index 468f28e..baec204 100644
--- a/service/java/com/android/server/wifi/util/EncryptedData.java
+++ b/service/java/com/android/server/wifi/util/EncryptedData.java
@@ -16,22 +16,23 @@
package com.android.server.wifi.util;
-import java.io.Serializable;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
/**
- * A class to store data created by {@link DataIntegrityChecker}.
+ * A class to store data created by {@link WifiConfigStoreEncryptionUtil}.
*/
-public class EncryptedData implements Serializable {
- private static final long serialVersionUID = 1337L;
+public class EncryptedData {
+ private final byte[] mEncryptedData;
+ private final byte[] mIv;
- private byte[] mEncryptedData;
- private byte[] mIv;
- private String mKeyAlias;
-
- public EncryptedData(byte[] encryptedData, byte[] iv, String keyAlias) {
+ public EncryptedData(byte[] encryptedData, byte[] iv) {
+ Preconditions.checkNotNull(encryptedData);
+ Preconditions.checkNotNull(iv);
mEncryptedData = encryptedData;
mIv = iv;
- mKeyAlias = keyAlias;
}
public byte[] getEncryptedData() {
@@ -42,7 +43,16 @@
return mIv;
}
- public String getKeyAlias() {
- return mKeyAlias;
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof EncryptedData)) return false;
+ EncryptedData otherEncryptedData = (EncryptedData) other;
+ return Arrays.equals(this.mEncryptedData, otherEncryptedData.mEncryptedData)
+ && Arrays.equals(this.mIv, otherEncryptedData.mIv);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(Arrays.hashCode(mEncryptedData), Arrays.hashCode(mIv));
}
}
diff --git a/service/java/com/android/server/wifi/util/ScanResultUtil.java b/service/java/com/android/server/wifi/util/ScanResultUtil.java
index 39e9d2c..b924838 100644
--- a/service/java/com/android/server/wifi/util/ScanResultUtil.java
+++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java
@@ -104,10 +104,10 @@
/**
* Helper method to check if the provided |scanResult| corresponds to PSK-SAE transition
- * network. This checks if the provided capabilities string contains PSK+SAE or not.
+ * network. This checks if the provided capabilities string contains both PSK and SAE or not.
*/
public static boolean isScanResultForPskSaeTransitionNetwork(ScanResult scanResult) {
- return scanResult.capabilities.contains("PSK+SAE");
+ return scanResult.capabilities.contains("PSK") && scanResult.capabilities.contains("SAE");
}
/**
diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java
index 4af40dd..3154df9 100644
--- a/service/java/com/android/server/wifi/util/TelephonyUtil.java
+++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java
@@ -22,6 +22,7 @@
import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
@@ -730,4 +731,43 @@
public static boolean isSimPresent(@Nonnull SubscriptionManager sm) {
return sm.getActiveSubscriptionIdList().length > 0;
}
+
+ /**
+ * Decorates a pseudonym with the NAI realm, in case it wasn't provided by the server
+ *
+ * @param tm TelephonyManager instance
+ * @param pseudonym The pseudonym (temporary identity) provided by the server
+ * @return pseudonym@realm which is based on current MCC/MNC, {@code null} if SIM is
+ * not ready or absent.
+ */
+ public static String decoratePseudonymWith3GppRealm(@NonNull TelephonyManager tm,
+ String pseudonym) {
+ if (tm == null || TextUtils.isEmpty(pseudonym)) {
+ return null;
+ }
+ if (pseudonym.contains("@")) {
+ // Pseudonym is already decorated
+ return pseudonym;
+ }
+ TelephonyManager defaultDataTm = tm.createForSubscriptionId(
+ SubscriptionManager.getDefaultDataSubscriptionId());
+ if (defaultDataTm.getSimState() != TelephonyManager.SIM_STATE_READY) {
+ return null;
+ }
+ String mccMnc = defaultDataTm.getSimOperator();
+ if (mccMnc == null || mccMnc.isEmpty()) {
+ return null;
+ }
+
+ // Extract mcc & mnc from mccMnc
+ String mcc = mccMnc.substring(0, 3);
+ String mnc = mccMnc.substring(3);
+
+ if (mnc.length() == 2) {
+ mnc = "0" + mnc;
+ }
+
+ String realm = String.format(THREE_GPP_NAI_REALM_FORMAT, mnc, mcc);
+ return String.format("%s@%s", pseudonym, realm);
+ }
}
diff --git a/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java
new file mode 100644
index 0000000..46bf0fe
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableEntryException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * Tools to help encrypt/decrypt
+ */
+public class WifiConfigStoreEncryptionUtil {
+ private static final String TAG = "WifiConfigStoreEncryptionUtil";
+
+ private static final String ALIAS_SUFFIX = ".data-encryption-key";
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_TAG_LENGTH = 128;
+ private static final int KEY_LENGTH = 256;
+ private static final String KEY_STORE = "AndroidKeyStore";
+
+ private final String mDataFileName;
+
+ /**
+ * Construct a new util to help {@link com.android.server.wifi.WifiConfigStore.StoreData}
+ * modules to encrypt/decrypt credential data written/read from this config store file.
+ *
+ * @param dataFileName The full path of the data file.
+ * @throws NullPointerException When data file is empty string.
+ */
+ public WifiConfigStoreEncryptionUtil(@NonNull String dataFileName) {
+ if (TextUtils.isEmpty(dataFileName)) {
+ throw new NullPointerException("dataFileName must not be null or the empty "
+ + "string");
+ }
+ mDataFileName = dataFileName;
+ }
+
+ private String getKeyAlias() {
+ return mDataFileName + ALIAS_SUFFIX;
+ }
+
+ /**
+ * Encrypt the provided data blob.
+ *
+ * @param data Data blob to be encrypted.
+ * @return Instance of {@link EncryptedData} containing the encrypted info.
+ */
+ public @Nullable EncryptedData encrypt(byte[] data) {
+ EncryptedData encryptedData = null;
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias());
+ if (secretKeyReference != null) {
+ cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
+ encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV());
+ } else {
+ reportException(new Exception("secretKeyReference is null."),
+ "secretKeyReference is null.");
+ }
+ } catch (NoSuchAlgorithmException e) {
+ reportException(e, "encrypt could not find the algorithm: " + CIPHER_ALGORITHM);
+ } catch (NoSuchPaddingException e) {
+ reportException(e, "encrypt had a padding exception");
+ } catch (InvalidKeyException e) {
+ reportException(e, "encrypt received an invalid key");
+ } catch (BadPaddingException e) {
+ reportException(e, "encrypt had a padding problem");
+ } catch (IllegalBlockSizeException e) {
+ reportException(e, "encrypt had an illegal block size");
+ }
+ return encryptedData;
+ }
+
+ /**
+ * Decrypt the original data blob from the provided {@link EncryptedData}.
+ *
+ * @param encryptedData Instance of {@link EncryptedData} containing the encrypted info.
+ * @return Original data blob that was encrypted.
+ */
+ public @Nullable byte[] decrypt(@NonNull EncryptedData encryptedData) {
+ byte[] decryptedData = null;
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, encryptedData.getIv());
+ SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias());
+ if (secretKeyReference != null) {
+ cipher.init(Cipher.DECRYPT_MODE, secretKeyReference, spec);
+ decryptedData = cipher.doFinal(encryptedData.getEncryptedData());
+ }
+ } catch (NoSuchAlgorithmException e) {
+ reportException(e, "decrypt could not find cipher algorithm " + CIPHER_ALGORITHM);
+ } catch (NoSuchPaddingException e) {
+ reportException(e, "decrypt could not find padding algorithm");
+ } catch (IllegalBlockSizeException e) {
+ reportException(e, "decrypt had a illegal block size");
+ } catch (BadPaddingException e) {
+ reportException(e, "decrypt had bad padding");
+ } catch (InvalidKeyException e) {
+ reportException(e, "decrypt had an invalid key");
+ } catch (InvalidAlgorithmParameterException e) {
+ reportException(e, "decrypt had an invalid algorithm parameter");
+ }
+ return decryptedData;
+ }
+
+ private SecretKey getOrCreateSecretKey(String keyAlias) {
+ SecretKey secretKey = null;
+ try {
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
+ if (keyStore.containsAlias(keyAlias)) { // The key exists in key store. Get the key.
+ KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
+ .getEntry(keyAlias, null);
+ if (secretKeyEntry != null) {
+ secretKey = secretKeyEntry.getSecretKey();
+ } else {
+ reportException(new Exception("keystore contains the alias and the secret key "
+ + "entry was null"),
+ "keystore contains the alias and the secret key entry was null");
+ }
+ } else { // The key does not exist in key store. Create the key and store it.
+ KeyGenerator keyGenerator = KeyGenerator
+ .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
+
+ KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setKeySize(KEY_LENGTH)
+ .setUid(Process.WIFI_UID)
+ .build();
+
+ keyGenerator.init(keyGenParameterSpec);
+ secretKey = keyGenerator.generateKey();
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ reportException(e, "getOrCreateSecretKey had an invalid algorithm parameter");
+ } catch (KeyStoreException e) {
+ reportException(e, "getOrCreateSecretKey cannot find the keystore: " + KEY_STORE);
+ } catch (NoSuchAlgorithmException e) {
+ reportException(e, "getOrCreateSecretKey cannot find algorithm");
+ } catch (NoSuchProviderException e) {
+ reportException(e, "getOrCreateSecretKey cannot find crypto provider");
+ } catch (UnrecoverableEntryException e) {
+ reportException(e, "getOrCreateSecretKey had an unrecoverable entry exception.");
+ }
+ return secretKey;
+ }
+
+ /* TODO(b/128526030): Remove this error reporting code upon resolving the bug. */
+ private static final boolean REQUEST_BUG_REPORT = false;
+ private void reportException(Exception exception, String error) {
+ Log.wtf(TAG, "An irrecoverable key store error was encountered: " + error, exception);
+ if (REQUEST_BUG_REPORT) {
+ SystemProperties.set("dumpstate.options", "bugreportwifi");
+ SystemProperties.set("ctl.start", "bugreport");
+ }
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
index 2834ad7..b1ceaf3 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -124,10 +124,11 @@
/**
* Checks whether than the target SDK of the package is less than the specified version code.
*/
- public boolean isTargetSdkLessThan(String packageName, int versionCode) {
+ public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
long ident = Binder.clearCallingIdentity();
try {
- if (mContext.getPackageManager().getApplicationInfo(packageName, 0).targetSdkVersion
+ if (mContext.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0, UserHandle.getUserId(callingUid)).targetSdkVersion
< versionCode) {
return true;
}
@@ -153,7 +154,7 @@
*/
public boolean checkCallersLocationPermission(String pkgName, int uid,
boolean coarseForTargetSdkLessThanQ) {
- boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q);
+ boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
index 188d3b5..db0f428 100644
--- a/service/java/com/android/server/wifi/util/XmlUtil.java
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -16,6 +16,7 @@
package com.android.server.wifi.util;
+import android.annotation.Nullable;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
@@ -28,6 +29,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -377,19 +379,51 @@
}
/**
+ * Write preshared key to the XML stream.
+ *
+ * If encryptionUtil is null or if encryption fails for some reason, the pre-shared
+ * key is stored in plaintext, else the encrypted psk is stored.
+ */
+ private static void writePreSharedKeyToXml(
+ XmlSerializer out, String preSharedKey,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
+ throws XmlPullParserException, IOException {
+ EncryptedData encryptedData = null;
+ if (encryptionUtil != null) {
+ if (preSharedKey != null) {
+ encryptedData = encryptionUtil.encrypt(preSharedKey.getBytes());
+ if (encryptedData == null) {
+ // We silently fail encryption failures!
+ Log.wtf(TAG, "Encryption of preSharedKey failed");
+ }
+ }
+ }
+ if (encryptedData != null) {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_PRE_SHARED_KEY);
+ EncryptedDataXmlUtil.writeToXml(out, encryptedData);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_PRE_SHARED_KEY);
+ } else {
+ XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, preSharedKey);
+ }
+ }
+
+ /**
* Write the Configuration data elements that are common for backup & config store to the
* XML stream.
*
- * @param out XmlSerializer instance pointing to the XML stream.
+ * @param out XmlSerializer instance pointing to the XML stream.
* @param configuration WifiConfiguration object to be serialized.
+ * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}. Backup/restore stores
+ * keys unencrypted.
*/
public static void writeCommonElementsToXml(
- XmlSerializer out, WifiConfiguration configuration)
+ XmlSerializer out, WifiConfiguration configuration,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
- XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, configuration.preSharedKey);
+ writePreSharedKeyToXml(out, configuration.preSharedKey, encryptionUtil);
writeWepKeysToXml(out, configuration.wepKeys);
XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
@@ -428,7 +462,7 @@
*/
public static void writeToXmlForBackup(XmlSerializer out, WifiConfiguration configuration)
throws XmlPullParserException, IOException {
- writeCommonElementsToXml(out, configuration);
+ writeCommonElementsToXml(out, configuration, null);
XmlUtil.writeNextValue(out, XML_TAG_METERED_OVERRIDE, configuration.meteredOverride);
}
@@ -436,13 +470,15 @@
* Write the Configuration data elements for config store from the provided Configuration
* to the XML stream.
*
- * @param out XmlSerializer instance pointing to the XML stream.
+ * @param out XmlSerializer instance pointing to the XML stream.
* @param configuration WifiConfiguration object to be serialized.
+ * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
*/
public static void writeToXmlForConfigStore(
- XmlSerializer out, WifiConfiguration configuration)
+ XmlSerializer out, WifiConfiguration configuration,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
- writeCommonElementsToXml(out, configuration);
+ writeCommonElementsToXml(out, configuration, encryptionUtil);
XmlUtil.writeNextValue(out, XML_TAG_STATUS, configuration.status);
XmlUtil.writeNextValue(out, XML_TAG_FQDN, configuration.FQDN);
XmlUtil.writeNextValue(
@@ -509,13 +545,16 @@
* Note: This is used for parsing both backup data and config store data. Looping through
* the tags make it easy to add or remove elements in the future versions if needed.
*
- * @param in XmlPullParser instance pointing to the XML stream.
+ * @param in XmlPullParser instance pointing to the XML stream.
* @param outerTagDepth depth of the outer tag in the XML document.
+ * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
+ * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
* @return Pair<Config key, WifiConfiguration object> if parsing is successful,
* null otherwise.
*/
public static Pair<String, WifiConfiguration> parseFromXml(
- XmlPullParser in, int outerTagDepth)
+ XmlPullParser in, int outerTagDepth, boolean shouldExpectEncryptedCredentials,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
WifiConfiguration configuration = new WifiConfiguration();
String configKeyInData = null;
@@ -523,147 +562,175 @@
// Loop through and parse out all the elements from the stream within this section.
while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
- String[] valueName = new String[1];
- Object value = XmlUtil.readCurrentValue(in, valueName);
- if (valueName[0] == null) {
- throw new XmlPullParserException("Missing value name");
- }
- switch (valueName[0]) {
- case XML_TAG_CONFIG_KEY:
- configKeyInData = (String) value;
- break;
- case XML_TAG_SSID:
- configuration.SSID = (String) value;
- break;
- case XML_TAG_BSSID:
- configuration.BSSID = (String) value;
- break;
- case XML_TAG_PRE_SHARED_KEY:
- configuration.preSharedKey = (String) value;
- break;
- case XML_TAG_WEP_KEYS:
- populateWepKeysFromXmlValue(value, configuration.wepKeys);
- break;
- case XML_TAG_WEP_TX_KEY_INDEX:
- configuration.wepTxKeyIndex = (int) value;
- break;
- case XML_TAG_HIDDEN_SSID:
- configuration.hiddenSSID = (boolean) value;
- break;
- case XML_TAG_REQUIRE_PMF:
- configuration.requirePMF = (boolean) value;
- break;
- case XML_TAG_ALLOWED_KEY_MGMT:
- byte[] allowedKeyMgmt = (byte[]) value;
- configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
- break;
- case XML_TAG_ALLOWED_PROTOCOLS:
- byte[] allowedProtocols = (byte[]) value;
- configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
- break;
- case XML_TAG_ALLOWED_AUTH_ALGOS:
- byte[] allowedAuthAlgorithms = (byte[]) value;
- configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
- break;
- case XML_TAG_ALLOWED_GROUP_CIPHERS:
- byte[] allowedGroupCiphers = (byte[]) value;
- configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
- break;
- case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
- byte[] allowedPairwiseCiphers = (byte[]) value;
- configuration.allowedPairwiseCiphers =
- BitSet.valueOf(allowedPairwiseCiphers);
- break;
- case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
- byte[] allowedGroupMgmtCiphers = (byte[]) value;
- configuration.allowedGroupManagementCiphers =
- BitSet.valueOf(allowedGroupMgmtCiphers);
- break;
- case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
- byte[] allowedSuiteBCiphers = (byte[]) value;
- configuration.allowedSuiteBCiphers =
- BitSet.valueOf(allowedSuiteBCiphers);
- break;
- case XML_TAG_SHARED:
- configuration.shared = (boolean) value;
- break;
- case XML_TAG_STATUS:
- int status = (int) value;
- // Any network which was CURRENT before reboot needs
- // to be restored to ENABLED.
- if (status == WifiConfiguration.Status.CURRENT) {
- status = WifiConfiguration.Status.ENABLED;
- }
- configuration.status = status;
- break;
- case XML_TAG_FQDN:
- configuration.FQDN = (String) value;
- break;
- case XML_TAG_PROVIDER_FRIENDLY_NAME:
- configuration.providerFriendlyName = (String) value;
- break;
- case XML_TAG_LINKED_NETWORKS_LIST:
- configuration.linkedConfigurations = (HashMap<String, Integer>) value;
- break;
- case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
- configuration.defaultGwMacAddress = (String) value;
- break;
- case XML_TAG_VALIDATED_INTERNET_ACCESS:
- configuration.validatedInternetAccess = (boolean) value;
- break;
- case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
- configuration.noInternetAccessExpected = (boolean) value;
- break;
- case XML_TAG_USER_APPROVED:
- configuration.userApproved = (int) value;
- break;
- case XML_TAG_METERED_HINT:
- configuration.meteredHint = (boolean) value;
- break;
- case XML_TAG_METERED_OVERRIDE:
- configuration.meteredOverride = (int) value;
- break;
- case XML_TAG_USE_EXTERNAL_SCORES:
- configuration.useExternalScores = (boolean) value;
- break;
- case XML_TAG_NUM_ASSOCIATION:
- configuration.numAssociation = (int) value;
- break;
- case XML_TAG_CREATOR_UID:
- configuration.creatorUid = (int) value;
- break;
- case XML_TAG_CREATOR_NAME:
- configuration.creatorName = (String) value;
- break;
- case XML_TAG_CREATION_TIME:
- configuration.creationTime = (String) value;
- break;
- case XML_TAG_LAST_UPDATE_UID:
- configuration.lastUpdateUid = (int) value;
- break;
- case XML_TAG_LAST_UPDATE_NAME:
- configuration.lastUpdateName = (String) value;
- break;
- case XML_TAG_LAST_CONNECT_UID:
- configuration.lastConnectUid = (int) value;
- break;
- case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
- configuration.isLegacyPasspointConfig = (boolean) value;
- break;
- case XML_TAG_ROAMING_CONSORTIUM_OIS:
- configuration.roamingConsortiumIds = (long[]) value;
- break;
- case XML_TAG_RANDOMIZED_MAC_ADDRESS:
- configuration.setRandomizedMacAddress(
- MacAddress.fromString((String) value));
- break;
- case XML_TAG_MAC_RANDOMIZATION_SETTING:
- configuration.macRandomizationSetting = (int) value;
- macRandomizationSettingExists = true;
- break;
- default:
- throw new XmlPullParserException(
- "Unknown value name found: " + valueName[0]);
+ if (in.getAttributeValue(null, "name") != null) {
+ // Value elements.
+ String[] valueName = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, valueName);
+ if (valueName[0] == null) {
+ throw new XmlPullParserException("Missing value name");
+ }
+ switch (valueName[0]) {
+ case XML_TAG_CONFIG_KEY:
+ configKeyInData = (String) value;
+ break;
+ case XML_TAG_SSID:
+ configuration.SSID = (String) value;
+ break;
+ case XML_TAG_BSSID:
+ configuration.BSSID = (String) value;
+ break;
+ case XML_TAG_PRE_SHARED_KEY:
+ configuration.preSharedKey = (String) value;
+ break;
+ case XML_TAG_WEP_KEYS:
+ populateWepKeysFromXmlValue(value, configuration.wepKeys);
+ break;
+ case XML_TAG_WEP_TX_KEY_INDEX:
+ configuration.wepTxKeyIndex = (int) value;
+ break;
+ case XML_TAG_HIDDEN_SSID:
+ configuration.hiddenSSID = (boolean) value;
+ break;
+ case XML_TAG_REQUIRE_PMF:
+ configuration.requirePMF = (boolean) value;
+ break;
+ case XML_TAG_ALLOWED_KEY_MGMT:
+ byte[] allowedKeyMgmt = (byte[]) value;
+ configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
+ break;
+ case XML_TAG_ALLOWED_PROTOCOLS:
+ byte[] allowedProtocols = (byte[]) value;
+ configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
+ break;
+ case XML_TAG_ALLOWED_AUTH_ALGOS:
+ byte[] allowedAuthAlgorithms = (byte[]) value;
+ configuration.allowedAuthAlgorithms = BitSet.valueOf(
+ allowedAuthAlgorithms);
+ break;
+ case XML_TAG_ALLOWED_GROUP_CIPHERS:
+ byte[] allowedGroupCiphers = (byte[]) value;
+ configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
+ break;
+ case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
+ byte[] allowedPairwiseCiphers = (byte[]) value;
+ configuration.allowedPairwiseCiphers =
+ BitSet.valueOf(allowedPairwiseCiphers);
+ break;
+ case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
+ byte[] allowedGroupMgmtCiphers = (byte[]) value;
+ configuration.allowedGroupManagementCiphers =
+ BitSet.valueOf(allowedGroupMgmtCiphers);
+ break;
+ case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
+ byte[] allowedSuiteBCiphers = (byte[]) value;
+ configuration.allowedSuiteBCiphers =
+ BitSet.valueOf(allowedSuiteBCiphers);
+ break;
+ case XML_TAG_SHARED:
+ configuration.shared = (boolean) value;
+ break;
+ case XML_TAG_STATUS:
+ int status = (int) value;
+ // Any network which was CURRENT before reboot needs
+ // to be restored to ENABLED.
+ if (status == WifiConfiguration.Status.CURRENT) {
+ status = WifiConfiguration.Status.ENABLED;
+ }
+ configuration.status = status;
+ break;
+ case XML_TAG_FQDN:
+ configuration.FQDN = (String) value;
+ break;
+ case XML_TAG_PROVIDER_FRIENDLY_NAME:
+ configuration.providerFriendlyName = (String) value;
+ break;
+ case XML_TAG_LINKED_NETWORKS_LIST:
+ configuration.linkedConfigurations = (HashMap<String, Integer>) value;
+ break;
+ case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
+ configuration.defaultGwMacAddress = (String) value;
+ break;
+ case XML_TAG_VALIDATED_INTERNET_ACCESS:
+ configuration.validatedInternetAccess = (boolean) value;
+ break;
+ case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
+ configuration.noInternetAccessExpected = (boolean) value;
+ break;
+ case XML_TAG_USER_APPROVED:
+ configuration.userApproved = (int) value;
+ break;
+ case XML_TAG_METERED_HINT:
+ configuration.meteredHint = (boolean) value;
+ break;
+ case XML_TAG_METERED_OVERRIDE:
+ configuration.meteredOverride = (int) value;
+ break;
+ case XML_TAG_USE_EXTERNAL_SCORES:
+ configuration.useExternalScores = (boolean) value;
+ break;
+ case XML_TAG_NUM_ASSOCIATION:
+ configuration.numAssociation = (int) value;
+ break;
+ case XML_TAG_CREATOR_UID:
+ configuration.creatorUid = (int) value;
+ break;
+ case XML_TAG_CREATOR_NAME:
+ configuration.creatorName = (String) value;
+ break;
+ case XML_TAG_CREATION_TIME:
+ configuration.creationTime = (String) value;
+ break;
+ case XML_TAG_LAST_UPDATE_UID:
+ configuration.lastUpdateUid = (int) value;
+ break;
+ case XML_TAG_LAST_UPDATE_NAME:
+ configuration.lastUpdateName = (String) value;
+ break;
+ case XML_TAG_LAST_CONNECT_UID:
+ configuration.lastConnectUid = (int) value;
+ break;
+ case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
+ configuration.isLegacyPasspointConfig = (boolean) value;
+ break;
+ case XML_TAG_ROAMING_CONSORTIUM_OIS:
+ configuration.roamingConsortiumIds = (long[]) value;
+ break;
+ case XML_TAG_RANDOMIZED_MAC_ADDRESS:
+ configuration.setRandomizedMacAddress(
+ MacAddress.fromString((String) value));
+ break;
+ case XML_TAG_MAC_RANDOMIZATION_SETTING:
+ configuration.macRandomizationSetting = (int) value;
+ macRandomizationSettingExists = true;
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ } else {
+ String tagName = in.getName();
+ if (tagName == null) {
+ throw new XmlPullParserException("Unexpected null tag found");
+ }
+ switch (tagName) {
+ case XML_TAG_PRE_SHARED_KEY:
+ if (!shouldExpectEncryptedCredentials || encryptionUtil == null) {
+ throw new XmlPullParserException(
+ "Encrypted preSharedKey section not expected");
+ }
+ EncryptedData encryptedData =
+ EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ byte[] preSharedKeyBytes = encryptionUtil.decrypt(encryptedData);
+ if (preSharedKeyBytes == null) {
+ Log.wtf(TAG, "Decryption of preSharedKey failed");
+ } else {
+ configuration.preSharedKey = new String(preSharedKeyBytes);
+ }
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown tag name found: " + tagName);
+ }
}
}
if (!macRandomizationSettingExists) {
@@ -1019,20 +1086,52 @@
public static final String XML_TAG_REALM = "Realm";
/**
+ * Write password key to the XML stream.
+ *
+ * If encryptionUtil is null or if encryption fails for some reason, the password is stored
+ * in plaintext, else the encrypted psk is stored.
+ */
+ private static void writePasswordToXml(
+ XmlSerializer out, String password,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
+ throws XmlPullParserException, IOException {
+ EncryptedData encryptedData = null;
+ if (encryptionUtil != null) {
+ if (password != null) {
+ encryptedData = encryptionUtil.encrypt(password.getBytes());
+ if (encryptedData == null) {
+ // We silently fail encryption failures!
+ Log.wtf(TAG, "Encryption of password failed");
+ }
+ }
+ }
+ if (encryptedData != null) {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_PASSWORD);
+ EncryptedDataXmlUtil.writeToXml(out, encryptedData);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_PASSWORD);
+ } else {
+ XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, password);
+ }
+ }
+
+ /**
* Write the WifiEnterpriseConfig data elements from the provided config to the XML
* stream.
*
- * @param out XmlSerializer instance pointing to the XML stream.
+ * @param out XmlSerializer instance pointing to the XML stream.
* @param enterpriseConfig WifiEnterpriseConfig object to be serialized.
+ * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
*/
- public static void writeToXml(XmlSerializer out, WifiEnterpriseConfig enterpriseConfig)
+ public static void writeToXml(XmlSerializer out, WifiEnterpriseConfig enterpriseConfig,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
XmlUtil.writeNextValue(out, XML_TAG_IDENTITY,
enterpriseConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY));
XmlUtil.writeNextValue(out, XML_TAG_ANON_IDENTITY,
enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY));
- XmlUtil.writeNextValue(out, XML_TAG_PASSWORD,
- enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY));
+ writePasswordToXml(
+ out, enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY),
+ encryptionUtil);
XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERT,
enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY));
XmlUtil.writeNextValue(out, XML_TAG_CA_CERT,
@@ -1060,15 +1159,170 @@
/**
* Parses the data elements from the provided XML stream to a WifiEnterpriseConfig object.
*
- * @param in XmlPullParser instance pointing to the XML stream.
+ * @param in XmlPullParser instance pointing to the XML stream.
* @param outerTagDepth depth of the outer tag in the XML document.
+ * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
+ * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
* @return WifiEnterpriseConfig object if parsing is successful, null otherwise.
*/
- public static WifiEnterpriseConfig parseFromXml(XmlPullParser in, int outerTagDepth)
+ public static WifiEnterpriseConfig parseFromXml(XmlPullParser in, int outerTagDepth,
+ boolean shouldExpectEncryptedCredentials,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
// Loop through and parse out all the elements from the stream within this section.
+ while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+ if (in.getAttributeValue(null, "name") != null) {
+ // Value elements.
+ String[] valueName = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, valueName);
+ if (valueName[0] == null) {
+ throw new XmlPullParserException("Missing value name");
+ }
+ switch (valueName[0]) {
+ case XML_TAG_IDENTITY:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
+ break;
+ case XML_TAG_ANON_IDENTITY:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
+ break;
+ case XML_TAG_PASSWORD:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
+ if (shouldExpectEncryptedCredentials
+ && !TextUtils.isEmpty(enterpriseConfig.getFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY))) {
+ // Indicates that encryption of password failed when it was last
+ // written.
+ Log.e(TAG, "password value not expected");
+ }
+ break;
+ case XML_TAG_CLIENT_CERT:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
+ break;
+ case XML_TAG_CA_CERT:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
+ break;
+ case XML_TAG_SUBJECT_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_ENGINE:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_KEY, (String) value);
+ break;
+ case XML_TAG_ENGINE_ID:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
+ break;
+ case XML_TAG_PRIVATE_KEY_ID:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
+ break;
+ case XML_TAG_ALT_SUBJECT_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_DOM_SUFFIX_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_CA_PATH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
+ break;
+ case XML_TAG_EAP_METHOD:
+ enterpriseConfig.setEapMethod((int) value);
+ break;
+ case XML_TAG_PHASE2_METHOD:
+ enterpriseConfig.setPhase2Method((int) value);
+ break;
+ case XML_TAG_PLMN:
+ enterpriseConfig.setPlmn((String) value);
+ break;
+ case XML_TAG_REALM:
+ enterpriseConfig.setRealm((String) value);
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ } else {
+ String tagName = in.getName();
+ if (tagName == null) {
+ throw new XmlPullParserException("Unexpected null tag found");
+ }
+ switch (tagName) {
+ case XML_TAG_PASSWORD:
+ if (!shouldExpectEncryptedCredentials || encryptionUtil == null) {
+ throw new XmlPullParserException(
+ "encrypted password section not expected");
+ }
+ EncryptedData encryptedData =
+ EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ byte[] passwordBytes = encryptionUtil.decrypt(encryptedData);
+ if (passwordBytes == null) {
+ Log.wtf(TAG, "Decryption of password failed");
+ } else {
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY,
+ new String(passwordBytes));
+ }
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown tag name found: " + tagName);
+ }
+ }
+ }
+ return enterpriseConfig;
+ }
+ }
+
+ /**
+ * Utility class to serialize and deseriaize {@link EncryptedData} object to XML &
+ * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
+ */
+ public static class EncryptedDataXmlUtil {
+ /**
+ * List of XML tags corresponding to EncryptedData object elements.
+ */
+ private static final String XML_TAG_ENCRYPTED_DATA = "EncryptedData";
+ private static final String XML_TAG_IV = "IV";
+
+ /**
+ * Write the NetworkSelectionStatus data elements from the provided status to the XML
+ * stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param encryptedData EncryptedData object to be serialized.
+ */
+ public static void writeToXml(XmlSerializer out, EncryptedData encryptedData)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ENCRYPTED_DATA, encryptedData.getEncryptedData());
+ XmlUtil.writeNextValue(out, XML_TAG_IV, encryptedData.getIv());
+ }
+
+ /**
+ * Parses the EncryptedData data elements from the provided XML stream to a
+ * EncryptedData object.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @return EncryptedData object if parsing is successful, null otherwise.
+ */
+ public static EncryptedData parseFromXml(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ byte[] encryptedData = null;
+ byte[] iv = null;
+
+ // Loop through and parse out all the elements from the stream within this section.
while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
String[] valueName = new String[1];
Object value = XmlUtil.readCurrentValue(in, valueName);
@@ -1076,72 +1330,18 @@
throw new XmlPullParserException("Missing value name");
}
switch (valueName[0]) {
- case XML_TAG_IDENTITY:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
+ case XML_TAG_ENCRYPTED_DATA:
+ encryptedData = (byte[]) value;
break;
- case XML_TAG_ANON_IDENTITY:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
- break;
- case XML_TAG_PASSWORD:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
- break;
- case XML_TAG_CLIENT_CERT:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
- break;
- case XML_TAG_CA_CERT:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
- break;
- case XML_TAG_SUBJECT_MATCH:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
- break;
- case XML_TAG_ENGINE:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.ENGINE_KEY, (String) value);
- break;
- case XML_TAG_ENGINE_ID:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
- break;
- case XML_TAG_PRIVATE_KEY_ID:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
- break;
- case XML_TAG_ALT_SUBJECT_MATCH:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
- break;
- case XML_TAG_DOM_SUFFIX_MATCH:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
- break;
- case XML_TAG_CA_PATH:
- enterpriseConfig.setFieldValue(
- WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
- break;
- case XML_TAG_EAP_METHOD:
- enterpriseConfig.setEapMethod((int) value);
- break;
- case XML_TAG_PHASE2_METHOD:
- enterpriseConfig.setPhase2Method((int) value);
- break;
- case XML_TAG_PLMN:
- enterpriseConfig.setPlmn((String) value);
- break;
- case XML_TAG_REALM:
- enterpriseConfig.setRealm((String) value);
+ case XML_TAG_IV:
+ iv = (byte[]) value;
break;
default:
throw new XmlPullParserException(
"Unknown value name found: " + valueName[0]);
}
}
- return enterpriseConfig;
+ return new EncryptedData(encryptedData, iv);
}
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index b64bf3d..afb3ef5 100644
--- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -384,7 +384,7 @@
@Mock AsyncChannel mNullAsyncChannel;
@Mock CarrierNetworkConfig mCarrierNetworkConfig;
@Mock Handler mNetworkAgentHandler;
-
+ @Mock ConnectionFailureNotifier mConnectionFailureNotifier;
final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor =
ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
@@ -441,6 +441,8 @@
when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard);
when(mWifiInjector.getWifiLockManager()).thenReturn(mWifiLockManager);
when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig);
+ when(mWifiInjector.makeConnectionFailureNotifier(any()))
+ .thenReturn(mConnectionFailureNotifier);
when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any()))
.thenReturn(Pair.create(Process.INVALID_UID, ""));
when(mWifiNative.initialize()).thenReturn(true);
@@ -1033,10 +1035,10 @@
/**
* Tests anonymous identity is set again whenever a connection is established for the carrier
- * that supports encrypted IMSI and anonymous identity.
+ * that supports encrypted IMSI and anonymous identity and no real pseudonym was provided.
*/
@Test
- public void testSetAnonymousIdentityWhenConnectionIsEstablished() throws Exception {
+ public void testSetAnonymousIdentityWhenConnectionIsEstablishedNoPseudonym() throws Exception {
mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork(
WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE));
when(mDataTelephonyManager.getSimOperator()).thenReturn("123456");
@@ -1047,6 +1049,58 @@
when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
+ // Initial value should be "not set"
+ assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+
+ triggerConnect();
+
+ // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm>
+ assertEquals(expectedAnonymousIdentity,
+ mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+ .thenReturn(mScanDetailCache);
+ when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq));
+ when(mScanDetailCache.getScanResult(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult());
+ when(mWifiNative.getEapAnonymousIdentity(anyString()))
+ .thenReturn(expectedAnonymousIdentity);
+
+ mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).getEapAnonymousIdentity(any());
+
+ // Post connection value should remain "not set"
+ assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+ // verify that WifiConfigManager#addOrUpdateNetwork() was called to clear any previously
+ // stored pseudonym. i.e. to enable Encrypted IMSI for subsequent connections.
+ // Note: This test will fail if future logic will have additional conditions that would
+ // trigger "add or update network" operation. The test needs to be updated to account for
+ // this change.
+ verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt());
+ }
+
+ /**
+ * Tests anonymous identity is set again whenever a connection is established for the carrier
+ * that supports encrypted IMSI and anonymous identity but real pseudonym was provided for
+ * subsequent connections.
+ */
+ @Test
+ public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithPseudonym()
+ throws Exception {
+ mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE));
+ when(mDataTelephonyManager.getSimOperator()).thenReturn("123456");
+ when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ mConnectedNetwork.enterpriseConfig.setAnonymousIdentity("");
+
+ String expectedAnonymousIdentity = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org";
+ String pseudonym = "83bcca9384fca@wlan.mnc456.mcc123.3gppnetwork.org";
+
+ when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
+
triggerConnect();
// CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm>
@@ -1059,19 +1113,74 @@
getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq));
when(mScanDetailCache.getScanResult(sBSSID)).thenReturn(
getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult());
+ when(mWifiNative.getEapAnonymousIdentity(anyString()))
+ .thenReturn(pseudonym);
mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
- // verify that WifiNative#getEapAnonymousIdentity() was never called since we are using
- // encrypted IMSI full authentication and not using pseudonym identity.
- verify(mWifiNative, never()).getEapAnonymousIdentity(any());
- // check that the anonymous identity remains anonymous@<realm> for subsequent connections.
- assertEquals(expectedAnonymousIdentity,
+ verify(mWifiNative).getEapAnonymousIdentity(any());
+ assertEquals(pseudonym,
mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+ // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
+ // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by
+ // pseudonym usage in all subsequent connections.
+ // Note: This test will fail if future logic will have additional conditions that would
+ // trigger "add or update network" operation. The test needs to be updated to account for
+ // this change.
+ verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt());
}
/**
+ * Tests anonymous identity is set again whenever a connection is established for the carrier
+ * that supports encrypted IMSI and anonymous identity but real but not decorated pseudonym was
+ * provided for subsequent connections.
+ */
+ @Test
+ public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithNonDecoratedPseudonym()
+ throws Exception {
+ mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE));
+ when(mDataTelephonyManager.getSimOperator()).thenReturn("123456");
+ when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ mConnectedNetwork.enterpriseConfig.setAnonymousIdentity("");
+
+ String realm = "wlan.mnc456.mcc123.3gppnetwork.org";
+ String expectedAnonymousIdentity = "anonymous";
+ String pseudonym = "83bcca9384fca";
+
+ when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
+
+ triggerConnect();
+
+ // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm>
+ assertEquals(expectedAnonymousIdentity + "@" + realm,
+ mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+ .thenReturn(mScanDetailCache);
+ when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq));
+ when(mScanDetailCache.getScanResult(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult());
+ when(mWifiNative.getEapAnonymousIdentity(anyString()))
+ .thenReturn(pseudonym);
+
+ mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).getEapAnonymousIdentity(any());
+ assertEquals(pseudonym + "@" + realm,
+ mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+ // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
+ // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by
+ // pseudonym usage in all subsequent connections.
+ // Note: This test will fail if future logic will have additional conditions that would
+ // trigger "add or update network" operation. The test needs to be updated to account for
+ // this change.
+ verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt());
+ }
+ /**
* Tests the Passpoint information is set in WifiInfo for Passpoint AP connection.
*/
@Test
@@ -1734,10 +1843,10 @@
/** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */
@Test
public void syncGetSupportedFeatures() {
- final int featureAware = WifiManager.WIFI_FEATURE_AWARE;
- final int featureInfra = WifiManager.WIFI_FEATURE_INFRA;
- final int featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
- final int featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
+ final long featureAware = WifiManager.WIFI_FEATURE_AWARE;
+ final long featureInfra = WifiManager.WIFI_FEATURE_INFRA;
+ final long featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
+ final long featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
final long featureLongBits = 0x1100000000L;
assertEquals(0, testGetSupportedFeaturesCase(0, false));
@@ -1811,15 +1920,15 @@
@Test
public void syncRemovePasspointConfig() throws Exception {
String fqdn = "test.com";
- when(mPasspointManager.removeProvider(fqdn)).thenReturn(true);
+ when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(true);
mLooper.startAutoDispatch();
- assertTrue(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, fqdn));
+ assertTrue(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, true, fqdn));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
- when(mPasspointManager.removeProvider(fqdn)).thenReturn(false);
+ when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(false);
mLooper.startAutoDispatch();
- assertFalse(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, fqdn));
+ assertFalse(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, true, fqdn));
mLooper.stopAutoDispatch();
}
@@ -1847,16 +1956,17 @@
config.setHomeSp(homeSp);
expectedConfigs.add(config);
- when(mPasspointManager.getProviderConfigs()).thenReturn(expectedConfigs);
+ when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean()))
+ .thenReturn(expectedConfigs);
mLooper.startAutoDispatch();
- assertEquals(expectedConfigs, mCmi.syncGetPasspointConfigs(mCmiAsyncChannel));
+ assertEquals(expectedConfigs, mCmi.syncGetPasspointConfigs(mCmiAsyncChannel, true));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
- when(mPasspointManager.getProviderConfigs())
- .thenReturn(new ArrayList<PasspointConfiguration>());
+ when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean()))
+ .thenReturn(new ArrayList<>());
mLooper.startAutoDispatch();
- assertTrue(mCmi.syncGetPasspointConfigs(mCmiAsyncChannel).isEmpty());
+ assertTrue(mCmi.syncGetPasspointConfigs(mCmiAsyncChannel, true).isEmpty());
mLooper.stopAutoDispatch();
}
@@ -2693,6 +2803,59 @@
}
/**
+ * Verify that we don't crash when WifiNative returns null as the current MAC address.
+ * @throws Exception
+ */
+ @Test
+ public void testMacRandomizationWifiNativeReturningNull() throws Exception {
+ when(mWifiNative.getMacAddress(anyString())).thenReturn(null);
+ initializeAndAddNetworkAndVerifySuccess();
+ assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest());
+ assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState());
+
+ connect();
+ verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS);
+ }
+
+ /**
+ * Verifies that a notification is posted when a connection failure happens on a network
+ * in the hotlist. Then verify that tapping on the notification launches an dialog, which
+ * could be used to set the randomization setting for a network to "Trusted".
+ */
+ @Test
+ public void testConnectionFailureSendRandomizationSettingsNotification() throws Exception {
+ when(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(anyInt())).thenReturn(true);
+ // Setup CONNECT_MODE & a WifiConfiguration
+ initializeAndAddNetworkAndVerifySuccess();
+ mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, sBSSID);
+ mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
+ WifiManager.ERROR_AUTH_FAILURE_TIMEOUT);
+ mLooper.dispatchAll();
+
+ WifiConfiguration config = mCmi.getCurrentWifiConfiguration();
+ verify(mConnectionFailureNotifier)
+ .showFailedToConnectDueToNoRandomizedMacSupportNotification(FRAMEWORK_NETWORK_ID);
+ }
+
+ /**
+ * Verifies that a notification is not posted when a wrong password failure happens on a
+ * network in the hotlist.
+ */
+ @Test
+ public void testNotCallingIsInFlakyRandomizationSsidHotlistOnWrongPassword() throws Exception {
+ when(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(anyInt())).thenReturn(true);
+ // Setup CONNECT_MODE & a WifiConfiguration
+ initializeAndAddNetworkAndVerifySuccess();
+ mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, FRAMEWORK_NETWORK_ID, 0, sBSSID);
+ mCmi.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
+ WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ mLooper.dispatchAll();
+
+ verify(mConnectionFailureNotifier, never())
+ .showFailedToConnectDueToNoRandomizedMacSupportNotification(anyInt());
+ }
+
+ /**
* Verifies that CMD_START_CONNECT make WifiDiagnostics report
* CONNECTION_EVENT_STARTED
* @throws Exception
@@ -3285,7 +3448,7 @@
when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(newLLStats);
mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
mLooper.dispatchAll();
- verify(mWifiDataStall).checkForDataStall(oldLLStats, newLLStats);
+ verify(mWifiDataStall).checkForDataStall(oldLLStats, newLLStats, mCmi.getWifiInfo());
verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(newLLStats);
}
@@ -3301,7 +3464,7 @@
WifiLinkLayerStats stats = new WifiLinkLayerStats();
when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats);
- when(mWifiDataStall.checkForDataStall(any(), any()))
+ when(mWifiDataStall.checkForDataStall(any(), any(), any()))
.thenReturn(WifiIsUnusableEvent.TYPE_UNKNOWN);
mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
mLooper.dispatchAll();
@@ -3309,11 +3472,16 @@
verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
eq(anyInt()), eq(-1));
- when(mWifiDataStall.checkForDataStall(any(), any()))
+ when(mWifiDataStall.checkForDataStall(any(), any(), any()))
.thenReturn(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
mLooper.dispatchAll();
verify(mWifiMetrics, times(2)).updateWifiUsabilityStatsEntries(any(), eq(stats));
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(10L + ClientModeImpl.DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS);
+ mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
+ mLooper.dispatchAll();
verify(mWifiMetrics).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1);
}
@@ -3484,14 +3652,15 @@
@Test
public void testRemovePasspointConfig() throws Exception {
String fqdn = "test.com";
- when(mPasspointManager.removeProvider(anyString())).thenReturn(true);
+ when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), anyString()))
+ .thenReturn(true);
// switch to connect mode and verify wifi is reported as enabled
startSupplicantAndDispatchMessages();
- mCmi.sendMessage(ClientModeImpl.CMD_REMOVE_PASSPOINT_CONFIG, fqdn);
+ mCmi.sendMessage(ClientModeImpl.CMD_REMOVE_PASSPOINT_CONFIG, TEST_UID, 0, fqdn);
mLooper.dispatchAll();
- verify(mWifiConfigManager).removePasspointConfiguredNetwork(eq(fqdn));
+ verify(mWifiConfigManager).removePasspointConfiguredNetwork(fqdn);
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/ConnectionFailureNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/ConnectionFailureNotifierTest.java
new file mode 100644
index 0000000..8bf07b8
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/ConnectionFailureNotifierTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 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 org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.os.Handler;
+import android.os.Process;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ConnectionFailureNotifier}.
+ */
+@SmallTest
+public class ConnectionFailureNotifierTest {
+ @Mock private Context mContext;
+ @Mock private WifiInjector mWifiInjector;
+ @Mock private Resources mResources;
+ @Mock private FrameworkFacade mFrameworkFacade;
+ @Mock private WifiConfigManager mWifiConfigManager;
+ @Mock private WifiConnectivityManager mWifiConnectivityManager;
+ @Mock private NotificationManager mNotificationManager;
+ @Mock private ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
+ @Mock private Notification mNotification;
+ @Mock private AlertDialog mAlertDialog;
+
+ final ArgumentCaptor<BroadcastReceiver> mBroadCastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ private ConnectionFailureNotifier mConnectionFailureNotifier;
+ TestLooper mLooper;
+
+ /** Initialize objects before each test run. */
+ @Before
+ public void setUp() throws Exception {
+ // Ensure looper exists
+ mLooper = new TestLooper();
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mWifiInjector.getNotificationManager()).thenReturn(mNotificationManager);
+ when(mWifiInjector.getConnectionFailureNotificationBuilder())
+ .thenReturn(mConnectionFailureNotificationBuilder);
+ when(mConnectionFailureNotificationBuilder
+ .buildNoMacRandomizationSupportNotification(any())).thenReturn(mNotification);
+ when(mConnectionFailureNotificationBuilder.buildChangeMacRandomizationSettingDialog(any(),
+ any())).thenReturn(mAlertDialog);
+ mConnectionFailureNotifier = new ConnectionFailureNotifier(mContext, mWifiInjector,
+ mFrameworkFacade, mWifiConfigManager, mWifiConnectivityManager,
+ new Handler(mLooper.getLooper()));
+
+ verify(mContext).registerReceiver(mBroadCastReceiverCaptor.capture(), any());
+ }
+
+ private class DisableMacRandomizationMatcher implements ArgumentMatcher<WifiConfiguration> {
+ @Override
+ public boolean matches(WifiConfiguration config) {
+ return config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE;
+ }
+ }
+
+ // Returns an intent that simulates the broadcast which is received when the user tap
+ // on the notification to change MAC randomization settings.
+ private Intent buildBroadcastForRandomizationSettingsDialog(WifiConfiguration config) {
+ Intent intent = mock(Intent.class);
+ when(intent.getAction()).thenReturn(ConnectionFailureNotificationBuilder
+ .ACTION_SHOW_SET_RANDOMIZATION_DETAILS);
+ when(intent.getIntExtra(eq(ConnectionFailureNotificationBuilder
+ .RANDOMIZATION_SETTINGS_NETWORK_ID), anyInt())).thenReturn(config.networkId);
+ when(intent.getStringExtra(
+ eq(ConnectionFailureNotificationBuilder.RANDOMIZATION_SETTINGS_NETWORK_SSID)))
+ .thenReturn(config.getSsidAndSecurityTypeString());
+ return intent;
+ }
+
+ /**
+ * Verify that a notification is posted when a connection failure happens on a network
+ * in the hotlist. Then verify that tapping on the notification launches an dialog, which
+ * could be used to set the randomization setting for a network to "Trusted".
+ */
+ @Test
+ public void testConnectionFailureSendRandomizationSettingsNotification() {
+ // Verify that the network is using randomized MAC at the start.
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ when(mWifiConfigManager.getConfiguredNetwork(config.networkId)).thenReturn(config);
+ assertEquals(WifiConfiguration.RANDOMIZATION_PERSISTENT, config.macRandomizationSetting);
+
+ mConnectionFailureNotifier.showFailedToConnectDueToNoRandomizedMacSupportNotification(
+ config.networkId);
+ // verify that a notification is sent
+ verify(mNotificationManager).notify(
+ eq(ConnectionFailureNotifier.NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID),
+ eq(mNotification));
+
+ // sets up the intent that simulates the user tapping on the notification.
+ Intent intent = buildBroadcastForRandomizationSettingsDialog(config);
+
+ // simulate the user tapping on the notification, then verify the dialog shows up, and
+ // the appropriate callback is registered
+ ArgumentCaptor<DialogInterface.OnClickListener> onClickListenerArgumentCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+ mBroadCastReceiverCaptor.getValue().onReceive(mContext, intent);
+ verify(mConnectionFailureNotificationBuilder).buildChangeMacRandomizationSettingDialog(
+ eq(config.SSID), onClickListenerArgumentCaptor.capture());
+
+ // simulate the user tapping on the option to reset MAC address to factory MAC
+ onClickListenerArgumentCaptor.getValue().onClick(null, 0);
+ mLooper.dispatchAll();
+
+ // verify the WifiConfiguration is updated properly.
+ verify(mWifiConfigManager).addOrUpdateNetwork(
+ argThat(new DisableMacRandomizationMatcher()), eq(Process.SYSTEM_UID));
+ // verify that we try to connect to the updated network.
+ verify(mWifiConnectivityManager).forceConnectivityScan(any());
+ }
+
+ /**
+ * Verify that if the WifiConfiguration if not found (may have been deleted by the timed the
+ * notification is tapped), then the AlertDialog does not show up.
+ */
+ @Test
+ public void testWifiConfigurationMismatch() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ when(mWifiConfigManager.getConfiguredNetwork(config.networkId)).thenReturn(config);
+ mConnectionFailureNotifier.showFailedToConnectDueToNoRandomizedMacSupportNotification(
+ config.networkId);
+ // verify that a notification is sent
+ verify(mNotificationManager).notify(
+ eq(ConnectionFailureNotifier.NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID),
+ any());
+
+ // sets up the intent that simulates the user tapping on the notification.
+ Intent intent = buildBroadcastForRandomizationSettingsDialog(config);
+
+ // the WifiConfiguration that is found doesn't match with the one received from broadcast.
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt()))
+ .thenReturn(WifiConfigurationTestUtil.createOpenNetwork());
+ mBroadCastReceiverCaptor.getValue().onReceive(mContext, intent);
+
+ // verify that the AlertDialog is not launched in this case
+ verify(mConnectionFailureNotificationBuilder, never())
+ .buildChangeMacRandomizationSettingDialog(any(), any());
+
+ verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any());
+ // instead we are showings a toast due to failing to find the network
+ verify(mFrameworkFacade).showToast(any(), any());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
index 702aa99..17b9d1c 100644
--- a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -78,7 +79,8 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mDeletedEphemeralSsidsStoreData.serializeData(out);
+ mDeletedEphemeralSsidsStoreData.serializeData(
+ out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -94,7 +96,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mDeletedEphemeralSsidsStoreData.deserializeData(in, in.getDepth());
+ mDeletedEphemeralSsidsStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
return mDeletedEphemeralSsidsStoreData.getSsidToTimeMap();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index bb71e4a..36da41b 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -107,7 +107,7 @@
private class HalDeviceManagerSpy extends HalDeviceManager {
HalDeviceManagerSpy() {
- super(mClock);
+ super(mClock, mTestLooper.getLooper());
}
@Override
diff --git a/tests/wifitests/src/com/android/server/wifi/MacAddressUtilTest.java b/tests/wifitests/src/com/android/server/wifi/MacAddressUtilTest.java
new file mode 100644
index 0000000..7e598db
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/MacAddressUtilTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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 org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.MacAddress;
+import android.net.wifi.WifiConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.security.ProviderException;
+import java.util.Random;
+
+import javax.crypto.Mac;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.MacAddressUtil}.
+ */
+@SmallTest
+public class MacAddressUtilTest {
+ private MacAddressUtil mMacAddressUtil;
+
+ @Mock private Mac mMac;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMacAddressUtil = new MacAddressUtil();
+ }
+
+ /**
+ * Verifies that calculatePersistentMacForConfiguration valid randomized MACs.
+ */
+ @Test
+ public void testCalculatePersistentMacForConfiguration() {
+ // verify null inputs
+ assertNull(mMacAddressUtil.calculatePersistentMacForConfiguration(null, null));
+
+ Random rand = new Random();
+ // Verify that a the MAC address calculated is valid
+ for (int i = 0; i < 10; i++) {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+
+ byte[] bytes = new byte[32];
+ rand.nextBytes(bytes);
+ when(mMac.doFinal(any())).thenReturn(bytes);
+ MacAddress macAddress = mMacAddressUtil.calculatePersistentMacForConfiguration(
+ config, mMac);
+ assertTrue(WifiConfiguration.isValidMacAddressForRandomization(macAddress));
+ }
+ }
+
+ /**
+ * Verify the java.security.ProviderException is caught.
+ */
+ @Test
+ public void testCalculatePersistentMacCatchesException() {
+ when(mMac.doFinal(any())).thenThrow(new ProviderException("error occurred"));
+ try {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertNull(mMacAddressUtil.calculatePersistentMacForConfiguration(config, mMac));
+ } catch (Exception e) {
+ fail("Exception not caught.");
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
index 7336c41..20b6c4f 100644
--- a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtilTest;
import org.junit.Before;
@@ -213,7 +214,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mNetworkListSharedStoreData.serializeData(out);
+ mNetworkListSharedStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -229,7 +230,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mNetworkListSharedStoreData.deserializeData(in, in.getDepth());
+ mNetworkListSharedStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
return mNetworkListSharedStoreData.getConfigurations();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java
index f40f71b..c0f0350 100644
--- a/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java
@@ -27,6 +27,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.wifi.WifiNetworkFactory.AccessPoint;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -80,7 +81,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mNetworkRequestStoreData.serializeData(out);
+ mNetworkRequestStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -92,7 +93,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mNetworkRequestStoreData.deserializeData(in, in.getDepth());
+ mNetworkRequestStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
index 5c1dcb4..a35c510 100644
--- a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
@@ -28,6 +28,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion;
import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -119,7 +120,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mNetworkSuggestionStoreData.serializeData(out);
+ mNetworkSuggestionStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -131,7 +132,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mNetworkSuggestionStoreData.deserializeData(in, in.getDepth());
+ mNetworkSuggestionStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java
index 4df560f..cdd4e6c 100644
--- a/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +63,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mRandomizedMacStoreData.serializeData(out);
+ mRandomizedMacStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -78,7 +79,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mRandomizedMacStoreData.deserializeData(in, in.getDepth());
+ mRandomizedMacStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
return mRandomizedMacStoreData.getMacMapping();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
index 9857898..38e2eaf 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
@@ -66,6 +66,11 @@
add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_2"));
}};
+ private static final List<WifiScanner.ScanSettings.HiddenNetwork> TEST_HIDDEN_NETWORKS_LIST_NS =
+ new ArrayList<WifiScanner.ScanSettings.HiddenNetwork>() {{
+ add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_3"));
+ add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_4"));
+ }};
@Mock private Context mContext;
@Mock private AppOpsManager mAppOps;
@@ -77,6 +82,8 @@
@Mock private WifiMetrics mWifiMetrics;
@Mock private Clock mClock;
@Mock private FrameworkFacade mFrameworkFacade;
+ @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
+
private ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor =
ArgumentCaptor.forClass(WorkSource.class);
private ArgumentCaptor<WifiScanner.ScanSettings> mScanSettingsArgumentCaptor =
@@ -98,7 +105,11 @@
MockitoAnnotations.initMocks(this);
when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
+ when(mWifiInjector.getWifiNetworkSuggestionsManager())
+ .thenReturn(mWifiNetworkSuggestionsManager);
when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(TEST_HIDDEN_NETWORKS_LIST);
+ when(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList())
+ .thenReturn(TEST_HIDDEN_NETWORKS_LIST_NS);
doNothing().when(mWifiScanner).registerScanListener(
mGlobalScanListenerArgumentCaptor.capture());
doNothing().when(mWifiScanner).startScan(
@@ -106,7 +117,8 @@
mScanRequestListenerArgumentCaptor.capture(),
mWorkSourceArgumentCaptor.capture());
- mInOrder = inOrder(mWifiScanner, mWifiConfigManager, mContext);
+ mInOrder = inOrder(mWifiScanner, mWifiConfigManager,
+ mContext, mWifiNetworkSuggestionsManager);
mTestScanDatas1 =
ScanTestUtil.createScanDatas(new int[][]{{ 2417, 2427, 5180, 5170 }},
new int[]{0},
@@ -205,8 +217,8 @@
mInOrder.verify(mWifiScanner).registerScanListener(any());
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
- assertTrue(mWorkSourceArgumentCaptor.getValue().equals(
- new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1)));
+ assertEquals(mWorkSourceArgumentCaptor.getValue(),
+ new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1));
validateScanSettings(mScanSettingsArgumentCaptor.getValue(), false, true);
}
@@ -222,10 +234,11 @@
assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
mInOrder.verify(mWifiConfigManager, never()).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiNetworkSuggestionsManager, never()).retrieveHiddenNetworkList();
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
- assertTrue(mWorkSourceArgumentCaptor.getValue().equals(
- new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1)));
+ assertEquals(mWorkSourceArgumentCaptor.getValue(),
+ new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1));
validateScanSettings(mScanSettingsArgumentCaptor.getValue(), false);
verify(mWifiMetrics).incrementExternalAppOneshotScanRequestsCount();
@@ -242,11 +255,13 @@
validateScanAvailableBroadcastSent(true);
assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
+
mInOrder.verify(mWifiConfigManager).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiNetworkSuggestionsManager).retrieveHiddenNetworkList();
mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
- assertTrue(mWorkSourceArgumentCaptor.getValue().equals(
- new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1)));
+ assertEquals(mWorkSourceArgumentCaptor.getValue(),
+ new WorkSource(TEST_UID, TEST_PACKAGE_NAME_1));
validateScanSettings(mScanSettingsArgumentCaptor.getValue(), true);
verify(mWifiMetrics).incrementExternalAppOneshotScanRequestsCount();
@@ -860,12 +875,15 @@
}
assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, scanSettings.reportEvents);
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
+ new ArrayList<>();
+ hiddenNetworkList.addAll(TEST_HIDDEN_NETWORKS_LIST);
+ hiddenNetworkList.addAll(TEST_HIDDEN_NETWORKS_LIST_NS);
if (expectHiddenNetworks) {
assertNotNull(scanSettings.hiddenNetworks);
- assertEquals(TEST_HIDDEN_NETWORKS_LIST.size(), scanSettings.hiddenNetworks.length);
+ assertEquals(hiddenNetworkList.size(), scanSettings.hiddenNetworks.length);
for (int i = 0; i < scanSettings.hiddenNetworks.length; i++) {
- validateHiddenNetworkInList(scanSettings.hiddenNetworks[i],
- TEST_HIDDEN_NETWORKS_LIST);
+ validateHiddenNetworkInList(scanSettings.hiddenNetworks[i], hiddenNetworkList);
}
} else {
assertNull(scanSettings.hiddenNetworks);
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index ed70ef5..c08adc1 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -89,6 +89,7 @@
@Mock WifiApConfigStore mWifiApConfigStore;
@Mock WifiMetrics mWifiMetrics;
@Mock SarManager mSarManager;
+ @Mock BaseWifiDiagnostics mWifiDiagnostics;
final ArgumentCaptor<WifiNative.InterfaceCallback> mWifiNativeInterfaceCallbackCaptor =
ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
final ArgumentCaptor<WifiNative.SoftApListener> mSoftApListenerCaptor =
@@ -136,7 +137,8 @@
mWifiApConfigStore,
config,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
return newSoftApManager;
@@ -209,7 +211,8 @@
mWifiApConfigStore,
nullApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -252,7 +255,8 @@
mWifiApConfigStore,
nullApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -294,7 +298,8 @@
mWifiApConfigStore,
nullApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -335,7 +340,8 @@
mWifiApConfigStore,
softApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -381,7 +387,8 @@
mWifiApConfigStore,
softApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -497,7 +504,8 @@
mWifiApConfigStore,
softApConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
mLooper.dispatchAll();
@@ -535,7 +543,8 @@
mWifiApConfigStore,
softApModeConfig,
mWifiMetrics,
- mSarManager);
+ mSarManager,
+ mWifiDiagnostics);
mLooper.dispatchAll();
newSoftApManager.start();
@@ -589,6 +598,7 @@
order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
verify(mSarManager).setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
+ verify(mWifiDiagnostics).stopLogging(TEST_INTERFACE_NAME);
order.verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
eq(UserHandle.ALL));
checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_DISABLED,
@@ -1097,6 +1107,7 @@
order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
order.verify(mCallback).onNumClientsChanged(0);
verify(mSarManager).setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
+ verify(mWifiDiagnostics).startLogging(TEST_INTERFACE_NAME);
verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(),
eq(UserHandle.ALL));
List<Intent> capturedIntents = intentCaptor.getAllValues();
diff --git a/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java
index ac6ae21..feedc0d 100644
--- a/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,6 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -80,7 +82,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mSsidSetStoreData.serializeData(out);
+ mSsidSetStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -95,7 +97,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mSsidSetStoreData.deserializeData(in, in.getDepth());
+ mSsidSetStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
index c814aef..df93eb4 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.google.android.collect.Sets;
@@ -74,7 +76,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mWakeupConfigData.serializeData(out);
+ mWakeupConfigData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -88,7 +90,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mWakeupConfigData.deserializeData(in, in.getDepth());
+ mWakeupConfigData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
@@ -177,7 +181,9 @@
*/
@Test
public void hasBeenReadIsTrueWhenUserStoreIsLoaded() throws Exception {
- mWakeupConfigData.deserializeData(null /* in */, 0 /* outerTagDepth */);
+ mWakeupConfigData.deserializeData(null /* in */, 0 /* outerTagDepth */,
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
assertTrue(mWakeupConfigData.hasBeenRead());
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
index 009429b..a004995 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,6 +39,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.wifi.util.ScanResultUtil;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -149,7 +151,9 @@
private void readUserStore() {
try {
- mWakeupConfigStoreData.deserializeData(null, 0);
+ mWakeupConfigStoreData.deserializeData(null, 0,
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
} catch (XmlPullParserException | IOException e) {
// unreachable
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 01ef909..0f48af9 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -42,6 +42,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -70,6 +71,7 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -135,7 +137,11 @@
@Mock private WifiConfigManager.OnSavedNetworkUpdateListener mWcmListener;
@Mock private FrameworkFacade mFrameworkFacade;
@Mock private CarrierNetworkConfig mCarrierNetworkConfig;
+ @Mock private MacAddressUtil mMacAddressUtil;
+ @Mock DeviceConfigFacade mDeviceConfigFacade;
+ final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor =
+ ArgumentCaptor.forClass(OnPropertiesChangedListener.class);
private MockResources mResources;
private InOrder mContextConfigStoreMockOrder;
private InOrder mNetworkListStoreDataMockOrder;
@@ -167,7 +173,10 @@
mResources.setInteger(
R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels,
TEST_MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCAN);
+ mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, true);
when(mContext.getResources()).thenReturn(mResources);
+ when(mDeviceConfigFacade.getRandomizationFlakySsidHotlist()).thenReturn(
+ Collections.emptySet());
// Setup UserManager profiles for the default user.
setupUserProfiles(TEST_DEFAULT_USER);
@@ -215,6 +224,10 @@
when(mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate())
.thenReturn(false);
when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig);
+ when(mWifiInjector.getMacAddressUtil()).thenReturn(mMacAddressUtil);
+ when(mMacAddressUtil.calculatePersistentMacForConfiguration(any(), any()))
+ .thenReturn(TEST_RANDOMIZED_MAC);
+
createWifiConfigManager();
mWifiConfigManager.setOnSavedNetworkUpdateListener(mWcmListener);
ArgumentCaptor<ContentObserver> observerCaptor =
@@ -230,13 +243,12 @@
// static mocking
mSession = ExtendedMockito.mockitoSession()
.mockStatic(WifiConfigStore.class, withSettings().lenient())
- .spyStatic(WifiConfigurationUtil.class)
.strictness(Strictness.LENIENT)
.startMocking();
- when(WifiConfigStore.createUserFiles(anyInt())).thenReturn(mock(List.class));
+ when(WifiConfigStore.createUserFiles(anyInt(), anyBoolean())).thenReturn(mock(List.class));
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager);
- when(WifiConfigurationUtil.calculatePersistentMacForConfiguration(any(), any()))
- .thenReturn(TEST_RANDOMIZED_MAC);
+ verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(),
+ mOnPropertiesChangedListenerCaptor.capture());
}
/**
@@ -295,8 +307,7 @@
*/
@Test
public void testRandomizedMacIsGeneratedEvenIfKeyStoreFails() {
- when(WifiConfigurationUtil.calculatePersistentMacForConfiguration(
- any(), any())).thenReturn(null);
+ when(mMacAddressUtil.calculatePersistentMacForConfiguration(any(), any())).thenReturn(null);
// Try adding a network.
WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
@@ -306,8 +317,11 @@
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworksWithPasswords();
- // Verify that despite KeyStore returning null, we are still getting a valid MAC address.
+ // Verify that we have attempted to generate the MAC address twice (1 retry)
+ verify(mMacAddressUtil, times(2)).calculatePersistentMacForConfiguration(any(), any());
assertEquals(1, retrievedNetworks.size());
+
+ // Verify that despite KeyStore returning null, we are still getting a valid MAC address.
assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS,
retrievedNetworks.get(0).getRandomizedMacAddress().toString());
}
@@ -1982,6 +1996,44 @@
}
/**
+ * Verifies that macRandomizationSetting is not masked out when MAC randomization is supported.
+ */
+ @Test
+ public void testGetConfiguredNetworksNotMaskMacRandomizationSetting() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(config);
+
+ MacAddress testMac = MacAddress.createRandomUnicastAddress();
+ mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), testMac);
+
+ // Verify macRandomizationSetting is not masked out when feature is supported.
+ List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID);
+ assertEquals(1, configs.size());
+ assertEquals(WifiConfiguration.RANDOMIZATION_PERSISTENT,
+ configs.get(0).macRandomizationSetting);
+ }
+
+ /**
+ * Verifies that macRandomizationSetting is masked out to WifiConfiguration.RANDOMIZATION_NONE
+ * when MAC randomization is not supported on the device.
+ */
+ @Test
+ public void testGetConfiguredNetworksMasksMacRandomizationSetting() {
+ mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, false);
+ createWifiConfigManager();
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(config);
+
+ MacAddress testMac = MacAddress.createRandomUnicastAddress();
+ mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), testMac);
+
+ // Verify macRandomizationSetting is masked out when feature is unsupported.
+ List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID);
+ assertEquals(1, configs.size());
+ assertEquals(WifiConfiguration.RANDOMIZATION_NONE, configs.get(0).macRandomizationSetting);
+ }
+
+ /**
* Verifies that passwords are masked out when we return external configs except when
* explicitly asked for them.
*/
@@ -4607,7 +4659,7 @@
mWifiPermissionsUtil, mWifiPermissionsWrapper, mWifiInjector,
mNetworkListSharedStoreData, mNetworkListUserStoreData,
mDeletedEphemeralSsidsStoreData, mRandomizedMacStoreData,
- mFrameworkFacade, mLooper.getLooper());
+ mFrameworkFacade, mLooper.getLooper(), mDeviceConfigFacade);
mWifiConfigManager.enableVerboseLogging(1);
}
@@ -5336,4 +5388,32 @@
assertFalse(mWifiConfigManager.getConfiguredNetwork(networkId)
.getNetworkSelectionStatus().isNetworkTemporaryDisabled());
}
+
+ /**
+ * Verifies that isInFlakyRandomizationSsidHotlist returns true if the network's SSID is in
+ * the hotlist and the network is using randomized MAC.
+ */
+ @Test
+ public void testFlakyRandomizationSsidHotlist() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+ int networkId = result.getNetworkId();
+
+ // should return false when there is nothing in the hotlist
+ assertFalse(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+
+ // add the network's SSID to the hotlist and verify the method returns true
+ Set<String> ssidHotlist = new HashSet<>();
+ ssidHotlist.add(openNetwork.SSID);
+ when(mDeviceConfigFacade.getRandomizationFlakySsidHotlist()).thenReturn(ssidHotlist);
+ mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
+ assertTrue(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+
+ // Now change the macRandomizationSetting to "trusted" and then verify
+ // isInFlakyRandomizationSsidHotlist returns false
+ openNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ NetworkUpdateResult networkUpdateResult = updateNetworkToWifiConfigManager(openNetwork);
+ assertNotEquals(WifiConfiguration.INVALID_NETWORK_ID, networkUpdateResult.getNetworkId());
+ assertFalse(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 64e762b..efa2d43 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -31,8 +31,12 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.wifi.WifiConfigStore.StoreData;
import com.android.server.wifi.WifiConfigStore.StoreFile;
+import com.android.server.wifi.util.EncryptedData;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
+import libcore.util.HexEncoding;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -49,6 +53,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
/**
* Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
@@ -64,7 +69,7 @@
private static final String TEST_DATA_XML_STRING_FORMAT =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
- + "<int name=\"Version\" value=\"1\" />\n"
+ + "<int name=\"Version\" value=\"3\" />\n"
+ "<NetworkList>\n"
+ "<Network>\n"
+ "<WifiConfiguration>\n"
@@ -127,19 +132,29 @@
+ "</DeletedEphemeralSSIDList>\n"
+ "</WifiConfigStoreData>\n";
- private static final String TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE =
+ private static final String TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<%s/>n"
+ "</WifiConfigStoreData>\n";
- private static final String TEST_DATA_XML_STRING_FORMAT_WITH_TWO_DATA_SOURCE =
+ private static final String TEST_DATA_XML_STRING_FORMAT_V1_WITH_TWO_DATA_SOURCE =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<%s/>n"
+ "<%s/>n"
+ "</WifiConfigStoreData>\n";
+ private static final String TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"2\" />\n"
+ + "<Integrity>\n"
+ + "<byte-array name=\"EncryptedData\" num=\"48\">%s</byte-array>\n"
+ + "<byte-array name=\"IV\" num=\"12\">%s</byte-array>\n"
+ + "</Integrity>\n"
+ + "<%s />\n"
+ + "</WifiConfigStoreData>\n";
// Test mocks
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
@@ -147,6 +162,7 @@
private TestLooper mLooper;
@Mock private Clock mClock;
@Mock private WifiMetrics mWifiMetrics;
+ @Mock private WifiConfigStoreEncryptionUtil mEncryptionUtil;
private MockStoreFile mSharedStore;
private MockStoreFile mUserStore;
private MockStoreFile mUserNetworkSuggestionsStore;
@@ -170,6 +186,10 @@
.thenReturn(mAlarmManager.getAlarmManager());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
+ when(mEncryptionUtil.encrypt(any(byte[].class)))
+ .thenReturn(new EncryptedData(new byte[0], new byte[0]));
+ when(mEncryptionUtil.decrypt(any(EncryptedData.class)))
+ .thenReturn(new byte[0]);
mSharedStore = new MockStoreFile(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
mUserStore = new MockStoreFile(WifiConfigStore.STORE_FILE_USER_GENERAL);
mUserNetworkSuggestionsStore =
@@ -402,9 +422,9 @@
// Ensure that we got the call to deserialize empty shared data, but no user data.
verify(sharedStoreData).resetData();
- verify(sharedStoreData).deserializeData(eq(null), anyInt());
+ verify(sharedStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
verify(userStoreData, never()).resetData();
- verify(userStoreData, never()).deserializeData(any(), anyInt());
+ verify(userStoreData, never()).deserializeData(any(), anyInt(), anyInt(), any());
}
/**
@@ -432,9 +452,9 @@
// Ensure that we got the call to deserialize empty shared & user data.
verify(userStoreData).resetData();
- verify(userStoreData).deserializeData(eq(null), anyInt());
+ verify(userStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
verify(sharedStoreData).resetData();
- verify(sharedStoreData).deserializeData(eq(null), anyInt());
+ verify(sharedStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
}
/**
@@ -591,11 +611,11 @@
assertTrue(mWifiConfigStore.registerStoreData(storeData2));
String fileContentsXmlStringWithOnlyStoreData1 =
- String.format(TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE, storeData1Name);
+ String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE, storeData1Name);
String fileContentsXmlStringWithOnlyStoreData2 =
- String.format(TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE, storeData2Name);
+ String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE, storeData2Name);
String fileContentsXmlStringWithStoreData1AndStoreData2 =
- String.format(TEST_DATA_XML_STRING_FORMAT_WITH_TWO_DATA_SOURCE,
+ String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_TWO_DATA_SOURCE,
storeData1Name, storeData2Name);
// Scenario 1: StoreData1 in shared store file.
@@ -609,9 +629,9 @@
mUserStore.storeRawDataToWrite(null);
mWifiConfigStore.read();
- verify(storeData1).deserializeData(notNull(), anyInt());
- verify(storeData1, never()).deserializeData(eq(null), anyInt());
- verify(storeData2).deserializeData(eq(null), anyInt());
+ verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
+ verify(storeData2).deserializeData(eq(null), anyInt(), anyInt(), any());
reset(storeData1, storeData2);
// Scenario 2: StoreData2 in user store file.
@@ -625,9 +645,9 @@
mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
mWifiConfigStore.read();
- verify(storeData1).deserializeData(eq(null), anyInt());
- verify(storeData2).deserializeData(notNull(), anyInt());
- verify(storeData2, never()).deserializeData(eq(null), anyInt());
+ verify(storeData1).deserializeData(eq(null), anyInt(), anyInt(), any());
+ verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
reset(storeData1, storeData2);
// Scenario 3: StoreData1 in shared store file & StoreData2 in user store file.
@@ -641,10 +661,10 @@
mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
mWifiConfigStore.read();
- verify(storeData1).deserializeData(notNull(), anyInt());
- verify(storeData1, never()).deserializeData(eq(null), anyInt());
- verify(storeData2).deserializeData(notNull(), anyInt());
- verify(storeData2, never()).deserializeData(eq(null), anyInt());
+ verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
+ verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
reset(storeData1, storeData2);
// Scenario 4: StoreData1 & StoreData2 in shared store file.
@@ -659,10 +679,10 @@
mUserStore.storeRawDataToWrite(null);
mWifiConfigStore.read();
- verify(storeData1).deserializeData(notNull(), anyInt());
- verify(storeData1, never()).deserializeData(eq(null), anyInt());
- verify(storeData2).deserializeData(notNull(), anyInt());
- verify(storeData2, never()).deserializeData(eq(null), anyInt());
+ verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
+ verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
+ verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
reset(storeData1, storeData2);
}
@@ -709,9 +729,9 @@
verify(userStoreNetworkSuggestionsData).hasNewDataToSerialize();
// Verify that we serialized data from the first 2 data source, but not from the last one.
- verify(sharedStoreData).serializeData(any());
- verify(userStoreData).serializeData(any());
- verify(userStoreNetworkSuggestionsData, never()).serializeData(any());
+ verify(sharedStoreData).serializeData(any(), any());
+ verify(userStoreData).serializeData(any(), any());
+ verify(userStoreNetworkSuggestionsData, never()).serializeData(any(), any());
}
/**
@@ -753,6 +773,98 @@
}
/**
+ * Tests the read API behaviour when the config store file is version 1.
+ * Expected behaviour: The read should be successful and send the data to the corresponding
+ * {@link StoreData} instance.
+ */
+ @Test
+ public void testReadVersion1StoreFile() throws Exception {
+ // Register data container.
+ StoreData sharedStoreData = mock(StoreData.class);
+ when(sharedStoreData.getStoreFileId())
+ .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
+ when(sharedStoreData.getName()).thenReturn(TEST_SHARE_DATA);
+ StoreData userStoreData = mock(StoreData.class);
+ when(userStoreData.getStoreFileId())
+ .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
+ when(userStoreData.getName()).thenReturn(TEST_USER_DATA);
+ mWifiConfigStore.registerStoreData(sharedStoreData);
+ mWifiConfigStore.registerStoreData(userStoreData);
+
+ // Read both share and user config store.
+ mWifiConfigStore.setUserStores(mUserStores);
+
+ // Now store some content in the shared and user data files.
+ mUserStore.storeRawDataToWrite(
+ String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE,
+ TEST_USER_DATA).getBytes());
+ mSharedStore.storeRawDataToWrite(
+ String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE,
+ TEST_SHARE_DATA).getBytes());
+
+ // Read and verify the data content in the store file (metadata stripped out) has been sent
+ // to the corresponding store data when integrity check passes.
+ mWifiConfigStore.read();
+ verify(sharedStoreData, times(1)).deserializeData(
+ any(XmlPullParser.class), anyInt(),
+ eq(WifiConfigStore.INITIAL_CONFIG_STORE_DATA_VERSION), any());
+ verify(userStoreData, times(1)).deserializeData(
+ any(XmlPullParser.class), anyInt(),
+ eq(WifiConfigStore.INITIAL_CONFIG_STORE_DATA_VERSION), any());
+ }
+
+ /**
+ * Tests the read API behaviour to ensure that the integrity data is parsed from the file.
+ */
+ @Test
+ public void testReadVersion2StoreFile() throws Exception {
+ byte[] encryptedData = new byte[0];
+ byte[] iv = new byte[0];
+ Random random = new Random();
+ random.nextBytes(encryptedData);
+ random.nextBytes(iv);
+
+ // Register data container.
+ StoreData sharedStoreData = mock(StoreData.class);
+ when(sharedStoreData.getStoreFileId())
+ .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
+ when(sharedStoreData.getName()).thenReturn(TEST_SHARE_DATA);
+ when(sharedStoreData.hasNewDataToSerialize()).thenReturn(true);
+ StoreData userStoreData = mock(StoreData.class);
+ when(userStoreData.getStoreFileId())
+ .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
+ when(userStoreData.getName()).thenReturn(TEST_USER_DATA);
+ when(userStoreData.hasNewDataToSerialize()).thenReturn(true);
+ mWifiConfigStore.registerStoreData(sharedStoreData);
+ mWifiConfigStore.registerStoreData(userStoreData);
+
+ // Read both share and user config store.
+ mWifiConfigStore.setUserStores(mUserStores);
+
+ // Now store some content in the shared and user data files with encrypted data from above.
+ mUserStore.storeRawDataToWrite(
+ String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
+ HexEncoding.encodeToString(encryptedData),
+ HexEncoding.encodeToString(iv),
+ TEST_USER_DATA).getBytes());
+ mSharedStore.storeRawDataToWrite(
+ String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
+ HexEncoding.encodeToString(encryptedData),
+ HexEncoding.encodeToString(iv),
+ TEST_SHARE_DATA).getBytes());
+
+ // Read and verify the data content in the store file (metadata stripped out) has been sent
+ // to the corresponding store data.
+ mWifiConfigStore.read();
+ verify(sharedStoreData, times(1))
+ .deserializeData(any(XmlPullParser.class), anyInt(),
+ eq(WifiConfigStore.INTEGRITY_CONFIG_STORE_DATA_VERSION), any());
+ verify(userStoreData, times(1))
+ .deserializeData(any(XmlPullParser.class), anyInt(),
+ eq(WifiConfigStore.INTEGRITY_CONFIG_STORE_DATA_VERSION), any());
+ }
+
+ /**
* Mock Store File to redirect all file writes from WifiConfigStore to local buffers.
* This can be used to examine the data output by WifiConfigStore.
*/
@@ -761,7 +873,7 @@
private boolean mStoreWritten;
MockStoreFile(@WifiConfigStore.StoreFileId int fileId) {
- super(new File("MockStoreFile"), fileId);
+ super(new File("MockStoreFile"), fileId, mEncryptionUtil);
}
@Override
@@ -812,13 +924,14 @@
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mData);
}
@Override
- public void deserializeData(XmlPullParser in, int outerTagDepth)
+ public void deserializeData(XmlPullParser in, int outerTagDepth, int version,
+ WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
if (in == null) {
return;
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 7f6c1bc..c1686b4 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -96,6 +96,10 @@
mWifiConnectivityHelper = mockWifiConnectivityHelper();
mWifiNS = mockWifiNetworkSelector();
when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
+ when(mWifiInjector.getWifiNetworkSuggestionsManager())
+ .thenReturn(mWifiNetworkSuggestionsManager);
+ when(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList())
+ .thenReturn(new ArrayList<>());
mWifiConnectivityManager = createConnectivityManager();
verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject());
mWifiConnectivityManager.setTrustedConnectionAllowed(true);
@@ -140,6 +144,7 @@
@Mock private CarrierNetworkConfig mCarrierNetworkConfig;
@Mock private WifiMetrics mWifiMetrics;
@Mock private WifiNetworkScoreCache mScoreCache;
+ @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
@Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor;
@Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor;
@Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor;
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
index a5f6852..71b6589 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
@@ -17,14 +17,17 @@
package com.android.server.wifi;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.os.Looper;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.Settings;
import androidx.test.filters.SmallTest;
@@ -33,6 +36,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -46,9 +50,15 @@
@Mock FrameworkFacade mFacade;
@Mock WifiMetrics mWifiMetrics;
WifiDataStall mWifiDataStall;
+ @Mock Clock mClock;
+ @Mock DeviceConfigFacade mDeviceConfigFacade;
+ @Mock Looper mClientModeImplLooper;
+ @Mock WifiInfo mWifiInfo;
private final WifiLinkLayerStats mOldLlStats = new WifiLinkLayerStats();
private final WifiLinkLayerStats mNewLlStats = new WifiLinkLayerStats();
+ final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor =
+ ArgumentCaptor.forClass(OnPropertiesChangedListener.class);
/**
* Sets up for unit test
@@ -64,21 +74,38 @@
Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX,
WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT))
.thenReturn(WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT);
+ when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ when(mDeviceConfigFacade.getDataStallTxTputThrMbps()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS);
+ when(mDeviceConfigFacade.getDataStallRxTputThrMbps()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS);
+ when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR);
+ when(mDeviceConfigFacade.getDataStallCcaLevelThr()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_CCA_LEVEL_THR);
+ when(mWifiInfo.getLinkSpeed()).thenReturn(100);
+ when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(100);
+ when(mWifiInfo.getFrequency()).thenReturn(5850);
+ when(mWifiInfo.getBSSID()).thenReturn("5G_WiFi");
- mWifiDataStall = new WifiDataStall(mContext, mFacade, mWifiMetrics);
+ mWifiDataStall = new WifiDataStall(mContext, mFacade, mWifiMetrics, mDeviceConfigFacade,
+ mClientModeImplLooper, mClock);
mOldLlStats.txmpdu_be = 1000;
- mOldLlStats.retries_be = 2000;
+ mOldLlStats.retries_be = 1000;
mOldLlStats.lostmpdu_be = 3000;
mOldLlStats.rxmpdu_be = 4000;
mOldLlStats.timeStampInMs = 10000;
- mNewLlStats.txmpdu_be = mOldLlStats.txmpdu_be;
- mNewLlStats.retries_be = mOldLlStats.retries_be;
+ mNewLlStats.txmpdu_be = 2 * mOldLlStats.txmpdu_be;
+ mNewLlStats.retries_be = 10 * mOldLlStats.retries_be;
mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be;
mNewLlStats.rxmpdu_be = mOldLlStats.rxmpdu_be;
mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs
+ WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL - 1;
+ verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(),
+ mOnPropertiesChangedListenerCaptor.capture());
}
/**
@@ -98,23 +125,53 @@
*/
@Test
public void verifyDataStallTxFailure() throws Exception {
- mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT;
- assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verify(mWifiMetrics).logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
}
/**
+ * Verify there is no data stall from tx failures if tx failures are not consecutively bad
+ */
+ @Test
+ public void verifyNoDataStallWhenTxFailureIsNotConsecutive() throws Exception {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
+ verifyUpdateWifiIsUnusableLinkLayerStats();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ mNewLlStats.retries_be = 2 * mOldLlStats.retries_be;
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
+ verify(mWifiMetrics, never()).logWifiIsUnusableEvent(
+ WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
+ }
+
+ /**
* Verify there is data stall from rx failures
*/
@Test
public void verifyDataStallRxFailure() throws Exception {
- mNewLlStats.txmpdu_be =
- mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT;
- assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(1);
+ mNewLlStats.retries_be = 2 * mOldLlStats.retries_be;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verify(mWifiMetrics).logWifiIsUnusableEvent(
WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX);
}
@@ -124,58 +181,61 @@
*/
@Test
public void verifyDataStallBothTxRxFailure() throws Exception {
- mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT;
- mNewLlStats.txmpdu_be =
- mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT;
- assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(1);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verify(mWifiMetrics).logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH);
}
/**
- * Verify that we can change the minimum number of tx failures
- * to trigger data stall from settings
+ * Verify that we can change the duration of evaluating Wifi conditions
+ * to trigger data stall from DeviceConfigFacade
*/
@Test
- public void verifyDataStallTxFailureSettingsChange() throws Exception {
- when(mFacade.getIntegerSetting(mContext,
- Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD,
- WifiDataStall.MIN_TX_BAD_DEFAULT))
- .thenReturn(WifiDataStall.MIN_TX_BAD_DEFAULT + 1);
- mWifiDataStall.loadSettings();
- verify(mWifiMetrics).setWifiDataStallMinTxBad(WifiDataStall.MIN_TX_BAD_DEFAULT + 1);
- verify(mWifiMetrics, times(2)).setWifiDataStallMinRxWithoutTx(
- WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT);
+ public void verifyDataStallDurationDeviceConfigChange() throws Exception {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+ when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS + 1);
+ mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
- mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT;
assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
- verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
+ verify(mWifiMetrics, never()).logWifiIsUnusableEvent(
+ WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
}
/**
- * Verify that we can change the minimum number of tx successes when rx success is 0
- * to trigger data stall from settings
+ * Verify that we can change the threshold of Tx packet error rate to trigger a data stall
+ * from DeviceConfigFacade
*/
@Test
- public void verifyDataStallRxFailureSettingsChange() throws Exception {
- when(mFacade.getIntegerSetting(mContext,
- Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX,
- WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT))
- .thenReturn(WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT + 1);
- mWifiDataStall.loadSettings();
- verify(mWifiMetrics, times(2)).setWifiDataStallMinTxBad(WifiDataStall.MIN_TX_BAD_DEFAULT);
- verify(mWifiMetrics).setWifiDataStallMinRxWithoutTx(
- WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT + 1);
+ public void verifyDataStallTxPerThrDeviceConfigChange() throws Exception {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(10L);
+ when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn(
+ DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR + 1);
+ mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
- mNewLlStats.txmpdu_be =
- mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT;
assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
- verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
+ assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
+ verify(mWifiMetrics, never()).logWifiIsUnusableEvent(
+ WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX);
}
/**
@@ -184,7 +244,7 @@
@Test
public void verifyNoDataStallWhenNoFail() throws Exception {
assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verify(mWifiMetrics, never()).resetWifiIsUnusableLinkLayerStats();
verifyUpdateWifiIsUnusableLinkLayerStats();
verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt());
@@ -196,11 +256,10 @@
*/
@Test
public void verifyNoDataStallBigTimeGap() throws Exception {
- mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT;
mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs
+ WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL + 1;
assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verifyUpdateWifiIsUnusableLinkLayerStats();
verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt());
}
@@ -212,7 +271,7 @@
public void verifyReset() throws Exception {
mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be - 1;
assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN,
- mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats));
+ mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo));
verify(mWifiMetrics).resetWifiIsUnusableLinkLayerStats();
verify(mWifiMetrics, never()).updateWifiIsUnusableLinkLayerStats(
anyLong(), anyLong(), anyLong(), anyLong(), anyLong());
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
index 6b63137..9883737 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
@@ -74,6 +74,8 @@
WifiDiagnostics mWifiDiagnostics;
private static final String FAKE_RING_BUFFER_NAME = "fake-ring-buffer";
+ private static final String STA_IF_NAME = "wlan0";
+ private static final String AP_IF_NAME = "wlan1";
private static final int SMALL_RING_BUFFER_SIZE_KB = 32;
private static final int LARGE_RING_BUFFER_SIZE_KB = 1024;
private static final int BYTES_PER_KBYTE = 1024;
@@ -137,8 +139,8 @@
/** Verifies that startLogging() registers a logging event handler. */
@Test
public void startLoggingRegistersLogEventHandler() throws Exception {
- final boolean verbosityToggle = false; // even default mode registers handler
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(false);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).setLoggingEventHandler(anyObject());
}
@@ -152,12 +154,15 @@
final boolean verbosityToggle = false; // even default mode registers handler
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).setLoggingEventHandler(anyObject());
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
reset(mWifiNative);
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).setLoggingEventHandler(anyObject());
}
@@ -165,14 +170,14 @@
@Test
public void startLoggingDoesNotRegisterLogEventHandlerIfPriorAttemptSucceeded()
throws Exception {
- final boolean verbosityToggle = false; // even default mode registers handler
-
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(false);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).setLoggingEventHandler(anyObject());
reset(mWifiNative);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(false);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative, never()).setLoggingEventHandler(anyObject());
}
@@ -186,7 +191,8 @@
@Test
public void startLoggingStopsAndRestartsRingBufferLoggingInVerboseMode() throws Exception {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).startLoggingRingBuffer(
eq(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
eq(FAKE_RING_BUFFER_NAME));
@@ -198,7 +204,8 @@
@Test
public void startLoggingStopsAndThenStartRingBufferLoggingInNormalMode() throws Exception {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
verify(mWifiNative).startLoggingRingBuffer(
eq(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
eq(FAKE_RING_BUFFER_NAME));
@@ -212,18 +219,19 @@
public void stopLoggingResetsLogHandlerIfHandlerWasRegistered() throws Exception {
final boolean verbosityToggle = false; // even default mode registers handler
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
reset(mWifiNative);
- mWifiDiagnostics.stopLogging();
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
verify(mWifiNative).resetLogHandler();
}
/** Verifies that, if a log handler is not registered, stopLogging() skips resetLogHandler(). */
@Test
public void stopLoggingOnlyResetsLogHandlerIfHandlerWasRegistered() throws Exception {
- mWifiDiagnostics.stopLogging();
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
verify(mWifiNative, never()).resetLogHandler();
}
@@ -233,15 +241,16 @@
final boolean verbosityToggle = false; // even default mode registers handler
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
reset(mWifiNative);
when(mWifiNative.resetLogHandler()).thenReturn(true);
- mWifiDiagnostics.stopLogging();
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
verify(mWifiNative).resetLogHandler();
reset(mWifiNative);
- mWifiDiagnostics.stopLogging();
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
verify(mWifiNative, never()).resetLogHandler();
}
@@ -251,7 +260,8 @@
@Test
public void canCaptureAndStoreRingBufferData() throws Exception {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
@@ -269,7 +279,8 @@
@Test
public void loggerDiscardsExtraneousData() throws Exception {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
final byte[] data1 = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
final byte[] data2 = {1, 2, 3};
@@ -282,34 +293,11 @@
assertArrayEquals(data2, ringBufferData[0]);
}
- /**
- * Verifies that, when verbose mode is not enabled, startLogging() calls
- * startPktFateMonitoring(any()).
- */
+ // Verifies that startPktFateMonitoring(any()) reports failure to start packet fate
@Test
- public void startLoggingStartsPacketFateWithoutVerboseMode() {
- final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
- verify(mWifiNative).startPktFateMonitoring(any());
- }
-
- /**
- * Verifies that, when verbose mode is enabled, startLogging() calls
- * startPktFateMonitoring(any()).
- */
- @Test
- public void startLoggingStartsPacketFateInVerboseMode() {
- final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
- verify(mWifiNative).startPktFateMonitoring(any());
- }
-
- // Verifies that startLogging() reports failure of startPktFateMonitoring(any()).
- @Test
- public void startLoggingReportsFailureOfStartPktFateMonitoring() {
- final boolean verbosityToggle = true;
+ public void startPktFateMonitoringReportsStartFailures() {
when(mWifiNative.startPktFateMonitoring(any())).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
verify(mLog).wC(contains("Failed"));
}
@@ -320,7 +308,8 @@
@Test
public void reportConnectionFailureIsIgnoredWithoutVerboseMode() {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -332,7 +321,8 @@
@Test
public void reportConnectionFailureFetchesFatesInVerboseMode() {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -341,7 +331,8 @@
@Test
public void reportConnectionEventPropagatesStartToLastMileLogger() {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_STARTED);
verify(mLastMileLogger).reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_STARTED);
}
@@ -349,7 +340,8 @@
@Test
public void reportConnectionEventPropagatesSuccessToLastMileLogger() {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
verify(mLastMileLogger).reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
}
@@ -357,7 +349,8 @@
@Test
public void reportConnectionEventPropagatesFailureToLastMileLogger() {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mLastMileLogger).reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
}
@@ -368,7 +361,8 @@
@Test
public void reportConnectionEventPropagatesTimeoutToLastMileLogger() {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_TIMEOUT);
verify(mLastMileLogger).reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_TIMEOUT);
}
@@ -380,7 +374,8 @@
public void loggerFetchesTxFatesEvenIfFetchingRxFatesFails() {
final boolean verbosityToggle = true;
when(mWifiNative.getRxPktFates(any(), anyObject())).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -393,7 +388,8 @@
public void loggerFetchesRxFatesEvenIfFetchingTxFatesFails() {
final boolean verbosityToggle = true;
when(mWifiNative.getTxPktFates(any(), anyObject())).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -405,7 +401,8 @@
final boolean verbosityToggle = false;
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -435,7 +432,8 @@
@Test
public void dumpSucceedsWhenFatesHaveBeenFetchedButAreEmpty() {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(any(), anyObject());
verify(mWifiNative).getRxPktFates(any(), anyObject());
@@ -452,7 +450,9 @@
}
private String getDumpString(boolean verbose) {
- mWifiDiagnostics.startLogging(verbose);
+ mWifiDiagnostics.enableVerboseLogging(verbose);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiNative.enableVerboseLogging(verbose ? 1 : 0);
when(mWifiNative.getTxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
public boolean answer(String ifaceName, WifiNative.TxFateReport[] fates) {
@@ -586,7 +586,8 @@
@Test
public void dumpOmitsFatesIfVerboseIsDisabledAfterFetch() {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startPktFateMonitoring(STA_IF_NAME);
when(mWifiNative.getTxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
public boolean answer(String ifaceName, WifiNative.TxFateReport[] fates) {
fates[0] = new WifiNative.TxFateReport(
@@ -610,7 +611,8 @@
verify(mWifiNative).getRxPktFates(any(), anyObject());
final boolean newVerbosityToggle = false;
- mWifiDiagnostics.startLogging(newVerbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(newVerbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -626,7 +628,8 @@
@Test
public void ringBufferSizeIsSmallByDefault() throws Exception {
final boolean verbosityToggle = false;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
@@ -641,7 +644,8 @@
when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
when(mBuildProperties.isEngBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
@@ -656,7 +660,8 @@
when(mBuildProperties.isEngBuild()).thenReturn(true);
when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
@@ -668,7 +673,8 @@
public void ringBufferSizeIsLargeInVerboseMode() throws Exception {
final boolean verbosityToggle = true;
- mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
@@ -678,8 +684,10 @@
/** Verifies that we use large ring buffers when switched from normal to verbose mode. */
@Test
public void startLoggingGrowsRingBuffersIfNeeded() throws Exception {
- mWifiDiagnostics.startLogging(false /* verbose disabled */);
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(false /* verbose disabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
@@ -691,12 +699,14 @@
@Test
public void startLoggingShrinksRingBuffersIfNeeded() throws Exception {
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
// Existing data is nuked (too large).
- mWifiDiagnostics.startLogging(false /* verbose disabled */);
+ mWifiDiagnostics.enableVerboseLogging(false /* verbose disabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(0, getLoggerRingBufferData().length);
@@ -734,7 +744,8 @@
/** Verifies that we capture the firmware and driver dumps if verbose is enabled. */
@Test
public void captureBugReportTakesFirmwareAndDriverDumpsInVerboseMode() {
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
verify(mWifiNative).getDriverStateDump();
@@ -745,7 +756,8 @@
public void dumpIncludesDriverStateDumpIfAvailable() {
when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
@@ -758,7 +770,8 @@
/** Verifies that the dump skips driver state, if driver state was not provided by HAL. */
@Test
public void dumpOmitsDriverStateDumpIfUnavailable() {
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
@@ -773,11 +786,13 @@
public void dumpOmitsDriverStateDumpIfVerboseDisabledAfterCapture() {
when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
- mWifiDiagnostics.startLogging(false /* verbose no longer enabled */);
+ mWifiDiagnostics.enableVerboseLogging(false /* verbose disabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -790,7 +805,8 @@
public void dumpIncludesFirmwareMemoryDumpIfAvailable() {
when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
@@ -803,7 +819,8 @@
/** Verifies that the dump skips firmware memory, if firmware memory was not provided by HAL. */
@Test
public void dumpOmitsFirmwareMemoryDumpIfUnavailable() {
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
@@ -818,11 +835,13 @@
public void dumpOmitsFirmwareMemoryDumpIfVerboseDisabledAfterCapture() {
when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.enableVerboseLogging(true /* verbose enabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
- mWifiDiagnostics.startLogging(false /* verbose no longer enabled */);
+ mWifiDiagnostics.enableVerboseLogging(false /* verbose disabled */);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -890,4 +909,130 @@
mWifiDiagnostics.captureAlertData(NON_FATAL_FW_ALART, ALERT_DATA);
verify(mWifiNative, never()).flushRingBufferData();
}
+
+ /**
+ * Verifies that we can capture ring-buffer data in SoftAp mode
+ */
+ @Test
+ public void canCaptureAndStoreRingBufferDataInSoftApMode() throws Exception {
+ final boolean verbosityToggle = false;
+
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(AP_IF_NAME);
+
+ final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData.length);
+ assertArrayEquals(data, ringBufferData[0]);
+ }
+
+ /**
+ * Verifies that we capture ring-buffer data in Station + SoftAp
+ * Concurrency mode.
+ */
+ @Test
+ public void canCaptureAndStoreRingBufferDataInConcurrencyMode() throws Exception {
+ final boolean verbosityToggle = false;
+
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
+ mWifiDiagnostics.startLogging(AP_IF_NAME);
+
+ final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData.length);
+ assertArrayEquals(data, ringBufferData[0]);
+ }
+
+ /**
+ * Verifies that we can continue to capture ring-buffer data
+ * after WiFi station is turned off in concurrency mode.
+ */
+ @Test
+ public void canCaptureAndStoreRingBufferDataAfterStaIsTurnedOffInConcurrencyMode()
+ throws Exception {
+ final boolean verbosityToggle = false;
+ final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
+
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
+ mWifiDiagnostics.startLogging(AP_IF_NAME);
+
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData0 = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData0.length);
+ assertArrayEquals(data, ringBufferData0[0]);
+
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
+
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData1 = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData1.length);
+ assertArrayEquals(data, ringBufferData1[0]);
+ }
+
+ /**
+ * Verifies that we can continue to capture ring-buffer data
+ * after SoftAp is turned off in concurrency mode.
+ */
+ @Test
+ public void canCaptureAndStoreRingBufferDataAfterSoftApIsTurnedOffInConcurrencyMode()
+ throws Exception {
+ final boolean verbosityToggle = false;
+ final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
+
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
+ mWifiDiagnostics.startLogging(AP_IF_NAME);
+
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData0 = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData0.length);
+ assertArrayEquals(data, ringBufferData0[0]);
+
+ mWifiDiagnostics.stopLogging(AP_IF_NAME);
+
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+
+ byte[][] ringBufferData1 = getLoggerRingBufferData();
+ assertEquals(1, ringBufferData1.length);
+ assertArrayEquals(data, ringBufferData1[0]);
+ }
+
+ /** Verifies that stoplogging on both the interfaces clean up
+ * all the resources.
+ */
+ @Test
+ public void verifyStopLoggingOnAllInterfacesClearTheResources() throws Exception {
+ final boolean verbosityToggle = false;
+
+ mWifiDiagnostics.enableVerboseLogging(verbosityToggle);
+ when(mWifiNative.setLoggingEventHandler(any())).thenReturn(true);
+ when(mWifiNative.resetLogHandler()).thenReturn(true);
+
+ mWifiDiagnostics.startLogging(STA_IF_NAME);
+ verify(mWifiNative).setLoggingEventHandler(any());
+
+ mWifiDiagnostics.startLogging(AP_IF_NAME);
+
+ mWifiDiagnostics.stopLogging(STA_IF_NAME);
+ verify(mWifiNative, never()).resetLogHandler();
+
+ mWifiDiagnostics.stopLogging(AP_IF_NAME);
+
+ verify(mWifiNative).resetLogHandler();
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
index 9ae3826..7c55228 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
@@ -21,16 +21,20 @@
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.*;
+import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiSsid;
+import android.os.Handler;
import android.os.test.TestLooper;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
@@ -42,6 +46,8 @@
*/
@SmallTest
public class WifiLastResortWatchdogTest {
+ final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor =
+ ArgumentCaptor.forClass(OnPropertiesChangedListener.class);
WifiLastResortWatchdog mLastResortWatchdog;
@Mock WifiInjector mWifiInjector;
@Mock WifiMetrics mWifiMetrics;
@@ -49,6 +55,8 @@
@Mock ClientModeImpl mClientModeImpl;
@Mock Clock mClock;
@Mock WifiInfo mWifiInfo;
+ @Mock Context mContext;
+ @Mock DeviceConfigFacade mDeviceConfigFacade;
private String[] mSsids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
private String[] mBssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
@@ -61,17 +69,24 @@
private boolean[] mHasEverConnected = {false, false, false, false};
private TestLooper mLooper;
private static final String TEST_NETWORK_SSID = "\"test_ssid\"";
+ private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = 30000;
@Before
public void setUp() throws Exception {
initMocks(this);
mLooper = new TestLooper();
when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery);
- mLastResortWatchdog = new WifiLastResortWatchdog(mWifiInjector, mClock, mWifiMetrics,
- mClientModeImpl, mLooper.getLooper());
+ when(mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(true);
+ when(mDeviceConfigFacade.getAbnormalConnectionDurationMs()).thenReturn(
+ DEFAULT_ABNORMAL_CONNECTION_DURATION_MS);
+ mLastResortWatchdog = new WifiLastResortWatchdog(mWifiInjector, mContext, mClock,
+ mWifiMetrics, mClientModeImpl, mLooper.getLooper(), mDeviceConfigFacade);
mLastResortWatchdog.setBugReportProbability(1);
when(mClientModeImpl.getWifiInfo()).thenReturn(mWifiInfo);
when(mWifiInfo.getSSID()).thenReturn(TEST_NETWORK_SSID);
+ when(mWifiInjector.getClientModeImplHandler()).thenReturn(mLastResortWatchdog.getHandler());
+ verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(),
+ mOnPropertiesChangedListenerCaptor.capture());
}
private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids,
@@ -2152,4 +2167,91 @@
verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses();
}
+ /**
+ * Verifies that when a connection takes too long (time difference between
+ * StaEvent.TYPE_CMD_START_CONNECT and StaEvent.TYPE_NETWORK_CONNECTION_EVENT) a bugreport is
+ * taken.
+ */
+ @Test
+ public void testAbnormalConnectionTimeTriggersBugreport() throws Exception {
+ // first verifies that bugreports are not taken when connection takes less than
+ // DEFAULT_ABNORMAL_CONNECTION_DURATION_MS
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(1L);
+ mLastResortWatchdog.noteStartConnectTime();
+ mLooper.dispatchAll();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ (long) DEFAULT_ABNORMAL_CONNECTION_DURATION_MS);
+ Handler handler = mLastResortWatchdog.getHandler();
+ handler.sendMessage(
+ handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null));
+ mLooper.dispatchAll();
+ verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString());
+
+ // Now verify that bugreport is taken
+ mLastResortWatchdog.noteStartConnectTime();
+ mLooper.dispatchAll();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 2L * DEFAULT_ABNORMAL_CONNECTION_DURATION_MS + 1);
+ handler.sendMessage(
+ handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null));
+ mLooper.dispatchAll();
+ verify(mClientModeImpl).takeBugReport(anyString(), anyString());
+
+ // Verify additional connections (without more TYPE_CMD_START_CONNECT) don't trigger more
+ // bugreports.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ 4L * DEFAULT_ABNORMAL_CONNECTION_DURATION_MS);
+ handler.sendMessage(
+ handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null));
+ mLooper.dispatchAll();
+ verify(mClientModeImpl).takeBugReport(anyString(), anyString());
+ }
+
+ /**
+ * Changes |mAbnormalConnectionDurationMs| to a new value, and then verify that a bugreport is
+ * taken for a connection that takes longer than the new threshold.
+ * @throws Exception
+ */
+ @Test
+ public void testGServicesSetDuration() throws Exception {
+ final int testDurationMs = 10 * 1000; // 10 seconds
+ // changes the abnormal connection duration to |testDurationMs|.
+ when(mDeviceConfigFacade.getAbnormalConnectionDurationMs()).thenReturn(testDurationMs);
+ mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
+
+ // verifies that bugreport is taken for connections that take longer than |testDurationMs|.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(1L);
+ mLastResortWatchdog.noteStartConnectTime();
+ mLooper.dispatchAll();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) testDurationMs + 2);
+ Handler handler = mLastResortWatchdog.getHandler();
+ handler.sendMessage(
+ handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null));
+ mLooper.dispatchAll();
+ verify(mClientModeImpl).takeBugReport(anyString(), anyString());
+ }
+
+ /**
+ * Verifies that bugreports are not triggered even when conditions are met after the
+ * |mAbnormalConnectionBugreportEnabled| flag is changed to false.
+ * @throws Exception
+ */
+ @Test
+ public void testGServicesFlagDisable() throws Exception {
+ // changes |mAbnormalConnectionBugreportEnabled| to false.
+ when(mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(false);
+ mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
+
+ // verifies that bugreports are not taken.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(1L);
+ mLastResortWatchdog.noteStartConnectTime();
+ mLooper.dispatchAll();
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ (long) DEFAULT_ABNORMAL_CONNECTION_DURATION_MS + 2);
+ Handler handler = mLastResortWatchdog.getHandler();
+ handler.sendMessage(
+ handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null));
+ mLooper.dispatchAll();
+ verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
index a40de55..255073b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
@@ -161,17 +161,6 @@
}
/**
- * Test to verify that the lock mode is verified before adding a lock.
- *
- * Steps: call acquireWifiLock with an invalid lock mode.
- * Expected: the call should throw an IllegalArgumentException.
- */
- @Test(expected = IllegalArgumentException.class)
- public void acquireWifiLockShouldThrowExceptionOnInivalidLockMode() throws Exception {
- mWifiLockManager.acquireWifiLock(WIFI_LOCK_MODE_INVALID, "", mBinder, mWorkSource);
- }
-
- /**
* Test that a call to acquireWifiLock with valid parameters works.
*
* Steps: call acquireWifiLock on the empty WifiLockManager.
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index e45906a..1176957 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -58,6 +58,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
@@ -1371,10 +1372,17 @@
when(networkDetail.getDtimInterval()).thenReturn(NETWORK_DETAIL_DTIM);
ScanResult scanResult = mock(ScanResult.class);
scanResult.level = SCAN_RESULT_LEVEL;
+ scanResult.capabilities = "EAP";
WifiConfiguration config = mock(WifiConfiguration.class);
config.SSID = "\"" + SSID + "\"";
config.dtimInterval = CONFIG_DTIM;
config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ config.allowedKeyManagement = new BitSet();
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ config.enterpriseConfig = new WifiEnterpriseConfig();
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2);
WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
mock(WifiConfiguration.NetworkSelectionStatus.class);
when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
@@ -1395,7 +1403,9 @@
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+ //Change configuration to open without randomization
config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ scanResult.capabilities = "";
//Create a connection event using the config and a scan detail
mWifiMetrics.startConnectionEvent(config, "Green",
WifiMetricsProto.ConnectionEvent.ROAM_NONE);
@@ -1411,8 +1421,20 @@
//Check that the correct values are being flowed through
assertEquals(2, mDecodedProto.connectionEvent.length);
assertEquals(CONFIG_DTIM, mDecodedProto.connectionEvent[0].routerFingerprint.dtim);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE,
+ mDecodedProto.connectionEvent[0].routerFingerprint.authentication);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.TYPE_EAP_TTLS,
+ mDecodedProto.connectionEvent[0].routerFingerprint.eapMethod);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_MSCHAPV2,
+ mDecodedProto.connectionEvent[0].routerFingerprint.authPhase2Method);
assertEquals(SCAN_RESULT_LEVEL, mDecodedProto.connectionEvent[0].signalStrength);
assertEquals(NETWORK_DETAIL_DTIM, mDecodedProto.connectionEvent[1].routerFingerprint.dtim);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.AUTH_OPEN,
+ mDecodedProto.connectionEvent[1].routerFingerprint.authentication);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.TYPE_EAP_UNKNOWN,
+ mDecodedProto.connectionEvent[1].routerFingerprint.eapMethod);
+ assertEquals(WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_NONE,
+ mDecodedProto.connectionEvent[1].routerFingerprint.authPhase2Method);
assertEquals(SCAN_RESULT_LEVEL, mDecodedProto.connectionEvent[1].signalStrength);
assertEquals(NETWORK_DETAIL_WIFIMODE,
mDecodedProto.connectionEvent[1].routerFingerprint.routerTechnology);
@@ -2577,6 +2599,45 @@
}
/**
+ * Verify that Tx and Rx per-band LinkSpeedCounts are correctly logged in metrics
+ */
+ @Test
+ public void testTxRxLinkSpeedBandCounts() throws Exception {
+ when(mFacade.getIntegerSetting(eq(mContext),
+ eq(Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED), anyInt())).thenReturn(1);
+ mWifiMetrics.loadSettings();
+ for (int i = 0; i < NUM_LINK_SPEED_LEVELS_TO_INCREMENT; i++) {
+ for (int j = 0; j <= i; j++) {
+ mWifiMetrics.incrementTxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS + i, RSSI_POLL_FREQUENCY);
+ mWifiMetrics.incrementRxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS + i + 1, RSSI_POLL_FREQUENCY);
+ }
+ }
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDecodedProto.txLinkSpeedCount2G.length);
+ assertEquals(0, mDecodedProto.rxLinkSpeedCount2G.length);
+ assertEquals(NUM_LINK_SPEED_LEVELS_TO_INCREMENT,
+ mDecodedProto.txLinkSpeedCount5GLow.length);
+ assertEquals(NUM_LINK_SPEED_LEVELS_TO_INCREMENT,
+ mDecodedProto.rxLinkSpeedCount5GLow.length);
+ assertEquals(0, mDecodedProto.txLinkSpeedCount5GMid.length);
+ assertEquals(0, mDecodedProto.rxLinkSpeedCount5GMid.length);
+ assertEquals(0, mDecodedProto.txLinkSpeedCount5GHigh.length);
+ assertEquals(0, mDecodedProto.rxLinkSpeedCount5GHigh.length);
+ for (int i = 0; i < NUM_LINK_SPEED_LEVELS_TO_INCREMENT; i++) {
+ assertEquals("Incorrect Tx link speed", WifiMetrics.MIN_LINK_SPEED_MBPS + i,
+ mDecodedProto.txLinkSpeedCount5GLow[i].key);
+ assertEquals("Incorrect Rx link speed", WifiMetrics.MIN_LINK_SPEED_MBPS + i + 1,
+ mDecodedProto.rxLinkSpeedCount5GLow[i].key);
+ assertEquals("Incorrect count of Tx link speed",
+ i + 1, mDecodedProto.txLinkSpeedCount5GLow[i].count);
+ assertEquals("Incorrect count of Rx link speed",
+ i + 1, mDecodedProto.rxLinkSpeedCount5GLow[i].count);
+ }
+ }
+
+ /**
* Verify that LinkSpeedCounts is not logged when disabled in settings
*/
@Test
@@ -2588,11 +2649,19 @@
for (int j = 0; j <= i; j++) {
mWifiMetrics.incrementLinkSpeedCount(
WifiMetrics.MIN_LINK_SPEED_MBPS + i, TEST_RSSI_LEVEL);
+ mWifiMetrics.incrementTxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS - i, RSSI_POLL_FREQUENCY);
+ mWifiMetrics.incrementRxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS - i, RSSI_POLL_FREQUENCY);
}
}
dumpProtoAndDeserialize();
assertEquals("LinkSpeedCounts should not be logged when disabled in settings",
0, mDecodedProto.linkSpeedCounts.length);
+ assertEquals("Tx LinkSpeedCounts should not be logged when disabled in settings",
+ 0, mDecodedProto.txLinkSpeedCount5GLow.length);
+ assertEquals("Rx LinkSpeedCounts should not be logged when disabled in settings",
+ 0, mDecodedProto.rxLinkSpeedCount5GLow.length);
}
/**
@@ -2608,6 +2677,10 @@
for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
mWifiMetrics.incrementLinkSpeedCount(
WifiMetrics.MIN_LINK_SPEED_MBPS - i, MIN_RSSI_LEVEL);
+ mWifiMetrics.incrementTxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS - i, RSSI_POLL_FREQUENCY);
+ mWifiMetrics.incrementRxLinkSpeedBandCount(
+ WifiMetrics.MIN_LINK_SPEED_MBPS - i, RSSI_POLL_FREQUENCY);
}
for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
mWifiMetrics.incrementLinkSpeedCount(
@@ -2620,6 +2693,10 @@
dumpProtoAndDeserialize();
assertEquals("LinkSpeedCounts should not be logged for out of bound values",
0, mDecodedProto.linkSpeedCounts.length);
+ assertEquals("Tx LinkSpeedCounts should not be logged for out of bound values",
+ 0, mDecodedProto.txLinkSpeedCount5GLow.length);
+ assertEquals("Rx LinkSpeedCounts should not be logged for out of bound values",
+ 0, mDecodedProto.rxLinkSpeedCount5GLow.length);
}
private int nextRandInt() {
@@ -2944,9 +3021,10 @@
dumpProtoAndDeserialize();
assertEquals(2 * WifiMetrics.MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD,
mDecodedProto.wifiUsabilityStatsList.length);
- for (int i = 0; i < mDecodedProto.wifiUsabilityStatsList.length; i++) {
+ for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD; i++) {
assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE,
- mDecodedProto.wifiUsabilityStatsList[i].stats.length);
+ mDecodedProto.wifiUsabilityStatsList[2 * i].stats.length);
+ assertEquals(2, mDecodedProto.wifiUsabilityStatsList[2 * i + 1].stats.length);
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
index d4e6594..d20c99c 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -61,11 +61,14 @@
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
+import android.util.Xml;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.wifi.WifiNetworkFactory.AccessPoint;
import com.android.server.wifi.nano.WifiMetricsProto;
import com.android.server.wifi.util.ScanResultUtil;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.WifiPermissionsUtil;
import org.junit.After;
@@ -76,9 +79,17 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -146,6 +157,7 @@
private WifiNetworkFactory mWifiNetworkFactory;
private NetworkRequestStoreData.DataSource mDataSource;
+ private NetworkRequestStoreData mNetworkRequestStoreData;
/**
* Setup the mocks.
@@ -164,7 +176,8 @@
.thenReturn(mConnectivityManager);
when(mPackageManager.getNameForUid(TEST_UID_1)).thenReturn(TEST_PACKAGE_NAME_1);
when(mPackageManager.getNameForUid(TEST_UID_2)).thenReturn(TEST_PACKAGE_NAME_2);
- when(mPackageManager.getApplicationInfo(any(), anyInt())).thenReturn(new ApplicationInfo());
+ when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(new ApplicationInfo());
when(mPackageManager.getApplicationLabel(any())).thenReturn(TEST_APP_NAME);
when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1))
.thenReturn(IMPORTANCE_FOREGROUND_SERVICE);
@@ -174,6 +187,7 @@
when(mWifiInjector.getClientModeImpl()).thenReturn(mClientModeImpl);
when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), anyString()))
.thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID_1));
+ when(mWifiScanner.getSingleScanResults()).thenReturn(Collections.emptyList());
mWifiNetworkFactory = new WifiNetworkFactory(mLooper.getLooper(), mContext,
mNetworkCapabilities, mActivityManager, mAlarmManager, mAppOpsManager, mClock,
@@ -185,6 +199,7 @@
verify(mWifiInjector).makeNetworkRequestStoreData(dataSourceArgumentCaptor.capture());
mDataSource = dataSourceArgumentCaptor.getValue();
assertNotNull(mDataSource);
+ mNetworkRequestStoreData = new NetworkRequestStoreData(mDataSource);
// Register and establish full connection to connectivity manager.
mWifiNetworkFactory.register();
@@ -940,16 +955,11 @@
verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
- ArgumentCaptor<List<ScanResult>> matchedScanResultsCaptor =
- ArgumentCaptor.forClass(List.class);
- verify(mNetworkRequestMatchCallback).onMatch(matchedScanResultsCaptor.capture());
-
- assertNotNull(matchedScanResultsCaptor.getValue());
// We expect no network match in this case.
- assertEquals(0, matchedScanResultsCaptor.getValue().size());
+ verify(mNetworkRequestMatchCallback, never()).onMatch(any());
- verify(mWifiMetrics).incrementNetworkRequestApiMatchSizeHistogram(
- matchedScanResultsCaptor.getValue().size());
+ // Don't increment metrics until we have a match
+ verify(mWifiMetrics, never()).incrementNetworkRequestApiMatchSizeHistogram(anyInt());
}
/**
@@ -985,13 +995,8 @@
verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
- ArgumentCaptor<List<ScanResult>> matchedScanResultsCaptor =
- ArgumentCaptor.forClass(List.class);
- verify(mNetworkRequestMatchCallback).onMatch(matchedScanResultsCaptor.capture());
-
- assertNotNull(matchedScanResultsCaptor.getValue());
// We expect no network match in this case.
- assertEquals(0, matchedScanResultsCaptor.getValue().size());
+ verify(mNetworkRequestMatchCallback, never()).onMatch(any());
}
/**
@@ -1083,6 +1088,73 @@
}
/**
+ * Verify when number of user approved access points exceed the capacity, framework should trim
+ * the Set by removing the least recently used elements.
+ */
+ @Test
+ public void testNetworkSpecifierHandleUserSelectionConnectToNetworkExceedApprovedListCapacity()
+ throws Exception {
+ int userApproveAccessPointCapacity = mWifiNetworkFactory.NUM_OF_ACCESS_POINT_LIMIT_PER_APP;
+ int numOfApPerSsid = userApproveAccessPointCapacity / 2 + 1;
+ String[] testIds = new String[]{TEST_SSID_1, TEST_SSID_2};
+
+ // Setup up scan data
+ setupScanDataSameSsidWithDiffBssid(SCAN_RESULT_TYPE_WPA_PSK, numOfApPerSsid, testIds);
+
+ // Setup network specifier for WPA-PSK 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 = WifiConfigurationTestUtil.createPskNetwork();
+ wifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1,
+ TEST_PACKAGE_NAME_1);
+
+ // request network, trigger scan and get matched set.
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
+ mNetworkRequestUserSelectionCallback.capture());
+
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ INetworkRequestUserSelectionCallback networkRequestUserSelectionCallback =
+ mNetworkRequestUserSelectionCallback.getValue();
+ assertNotNull(networkRequestUserSelectionCallback);
+
+ // Now trigger user selection to one of the network.
+ mSelectedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ mSelectedNetwork.SSID = "\"" + mTestScanDatas[0].getResults()[0].SSID + "\"";
+ networkRequestUserSelectionCallback.select(mSelectedNetwork);
+ mLooper.dispatchAll();
+
+ // Verifier num of Approved access points.
+ assertEquals(mWifiNetworkFactory.mUserApprovedAccessPointMap
+ .get(TEST_PACKAGE_NAME_1).size(), numOfApPerSsid);
+
+ // Now trigger user selection to another network with different SSID.
+ mSelectedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ mSelectedNetwork.SSID = "\"" + mTestScanDatas[0].getResults()[numOfApPerSsid].SSID + "\"";
+ networkRequestUserSelectionCallback.select(mSelectedNetwork);
+ mLooper.dispatchAll();
+
+ // Verify triggered trim when user Approved Access Points exceed capacity.
+ Set<AccessPoint> userApprovedAccessPoints = mWifiNetworkFactory.mUserApprovedAccessPointMap
+ .get(TEST_PACKAGE_NAME_1);
+ assertEquals(userApprovedAccessPoints.size(), userApproveAccessPointCapacity);
+ long numOfSsid1Aps = userApprovedAccessPoints
+ .stream()
+ .filter(a->a.ssid.equals(TEST_SSID_1))
+ .count();
+ assertEquals(numOfSsid1Aps, userApproveAccessPointCapacity - numOfApPerSsid);
+ }
+
+ /**
* Verify handling of user selection to trigger connection to an existing saved network.
*/
@Test
@@ -1537,9 +1609,17 @@
verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
// Now release the network request.
+ WifiConfiguration wcmNetwork = new WifiConfiguration(mSelectedNetwork);
+ wcmNetwork.networkId = TEST_NETWORK_ID_1;
+ wcmNetwork.creatorUid = TEST_UID_1;
+ wcmNetwork.fromWifiNetworkSpecifier = true;
+ wcmNetwork.ephemeral = true;
+ when(mWifiConfigManager.getConfiguredNetwork(mSelectedNetwork.configKey()))
+ .thenReturn(wcmNetwork);
mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
// Verify that we triggered a disconnect.
verify(mClientModeImpl, times(2)).disconnectCommand();
+ verify(mWifiConfigManager).removeNetwork(TEST_NETWORK_ID_1, TEST_UID_1);
// Re-enable connectivity manager .
verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
}
@@ -1598,6 +1678,7 @@
mLooper.dispatchAll();
verify(mNetworkRequestMatchCallback).onAbort();
+ verify(mWifiScanner, times(2)).getSingleScanResults();
verify(mWifiScanner, times(2)).startScan(any(), any(), any());
verifyUnfullfillableDispatched(mConnectivityMessenger);
@@ -1639,6 +1720,7 @@
mLooper.dispatchAll();
verify(mNetworkRequestMatchCallback).onAbort();
+ verify(mWifiScanner, times(2)).getSingleScanResults();
verify(mWifiScanner, times(2)).startScan(any(), any(), any());
verify(mAlarmManager).cancel(mPeriodicScanListenerArgumentCaptor.getValue());
verifyUnfullfillableDispatched(mConnectivityMessenger);
@@ -1673,6 +1755,7 @@
verify(mNetworkRequestMatchCallback).onAbort();
verify(mWifiConnectivityManager, times(1)).setSpecificNetworkRequestInProgress(true);
+ verify(mWifiScanner, times(2)).getSingleScanResults();
verify(mWifiScanner, times(2)).startScan(any(), any(), any());
verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
@@ -1712,6 +1795,7 @@
mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
verify(mWifiConnectivityManager, times(1)).setSpecificNetworkRequestInProgress(true);
+ verify(mWifiScanner, times(2)).getSingleScanResults();
verify(mWifiScanner, times(2)).startScan(any(), any(), any());
// we shouldn't disconnect until the user accepts the next request.
verify(mClientModeImpl, times(1)).disconnectCommand();
@@ -2004,10 +2088,10 @@
/**
* Verify the user approval bypass for a specific request for an access point that was already
- * approved previously.
+ * approved previously with no cached scan results matching.
*/
@Test
- public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchPreviouslyApproved()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchApprovedWithNoCache()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2027,6 +2111,9 @@
WifiConfigurationTestUtil.createPskNetwork(), TEST_UID_1, TEST_PACKAGE_NAME_1);
mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ validateUiStartParams(true);
+
mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
TEST_CALLBACK_IDENTIFIER);
// Trigger scan results & ensure we triggered a connect.
@@ -2048,8 +2135,7 @@
* approved previously, but then the user forgot it sometime after.
*/
@Test
- public void
- testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchPreviouslyApprovedNForgot()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchApprovedNForgot()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2093,7 +2179,7 @@
* not approved previously.
*/
@Test
- public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchNotPreviouslyApproved()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchNotApproved()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2134,7 +2220,7 @@
* (not access point) that was approved previously.
*/
@Test
- public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchPreviouslyApproved()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchApproved()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2322,6 +2408,188 @@
verify(mClientModeImpl).sendMessage(any());
}
+ /**
+ * Verify the config store save and load could preserve the elements order.
+ */
+ @Test
+ public void testStoreConfigSaveAndLoadPreserveOrder() throws Exception {
+ LinkedHashSet<AccessPoint> approvedApSet = new LinkedHashSet<>();
+ approvedApSet.add(new AccessPoint(TEST_SSID_1,
+ MacAddress.fromString(TEST_BSSID_1), WifiConfiguration.SECURITY_TYPE_PSK));
+ approvedApSet.add(new AccessPoint(TEST_SSID_2,
+ MacAddress.fromString(TEST_BSSID_2), WifiConfiguration.SECURITY_TYPE_PSK));
+ approvedApSet.add(new AccessPoint(TEST_SSID_3,
+ MacAddress.fromString(TEST_BSSID_3), WifiConfiguration.SECURITY_TYPE_PSK));
+ approvedApSet.add(new AccessPoint(TEST_SSID_4,
+ MacAddress.fromString(TEST_BSSID_4), WifiConfiguration.SECURITY_TYPE_PSK));
+ mWifiNetworkFactory.mUserApprovedAccessPointMap.put(TEST_PACKAGE_NAME_1,
+ new LinkedHashSet<>(approvedApSet));
+ // Save config.
+ byte[] xmlData = serializeData();
+ mWifiNetworkFactory.mUserApprovedAccessPointMap.clear();
+ // Load config.
+ deserializeData(xmlData);
+
+ LinkedHashSet<AccessPoint> storedApSet = mWifiNetworkFactory
+ .mUserApprovedAccessPointMap.get(TEST_PACKAGE_NAME_1);
+ // Check load config success and order preserved.
+ assertNotNull(storedApSet);
+ assertArrayEquals(approvedApSet.toArray(), storedApSet.toArray());
+ }
+
+ /**
+ * Verify the user approval bypass for a specific request for an access point that was already
+ * approved previously and the scan result is present in the cached scan results.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchApprovedWithCache()
+ throws Exception {
+ // 1. First request (no user approval bypass)
+ sendNetworkRequestAndSetupForConnectionStatus();
+
+ mWifiNetworkFactory.removeCallback(TEST_CALLBACK_IDENTIFIER);
+ reset(mNetworkRequestMatchCallback, mWifiScanner, mAlarmManager, mClientModeImpl);
+
+ // 2. Second request for the same access point (user approval bypass).
+ ScanResult matchingScanResult = mTestScanDatas[0].getResults()[0];
+ // simulate no cache expiry
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+ // Simulate the cached results matching.
+ when(mWifiScanner.getSingleScanResults())
+ .thenReturn(Arrays.asList(mTestScanDatas[0].getResults()));
+
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.fromString(matchingScanResult.BSSID),
+ MacAddress.BROADCAST_ADDRESS);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch,
+ WifiConfigurationTestUtil.createPskNetwork(), TEST_UID_1, TEST_PACKAGE_NAME_1);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Verify we did not trigger the UI for the second request.
+ verify(mContext, times(1)).startActivityAsUser(any(), any());
+ // Verify we did not trigger a scan.
+ verify(mWifiScanner, never()).startScan(any(), any(), any());
+ // Verify we did not trigger the match callback.
+ verify(mNetworkRequestMatchCallback, never()).onMatch(anyList());
+ // Verify that we sent a connection attempt to ClientModeImpl
+ verify(mClientModeImpl).sendMessage(any());
+
+ verify(mWifiMetrics).incrementNetworkRequestApiNumUserApprovalBypass();
+ }
+
+ /**
+ * Verify the user approval bypass for a specific request for an access point that was already
+ * approved previously and the scan result is present in the cached scan results, but the
+ * results are stale.
+ */
+ @Test
+ public void
+ testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchApprovedWithStaleCache()
+ throws Exception {
+ // 1. First request (no user approval bypass)
+ sendNetworkRequestAndSetupForConnectionStatus();
+
+ mWifiNetworkFactory.removeCallback(TEST_CALLBACK_IDENTIFIER);
+ reset(mNetworkRequestMatchCallback, mWifiScanner, mAlarmManager, mClientModeImpl);
+
+ long scanResultsTimestampInUs = 39484839202L;
+ mTestScanDatas[0].getResults()[0].timestamp = scanResultsTimestampInUs;
+ mTestScanDatas[0].getResults()[1].timestamp = scanResultsTimestampInUs;
+ mTestScanDatas[0].getResults()[2].timestamp = scanResultsTimestampInUs;
+ mTestScanDatas[0].getResults()[3].timestamp = scanResultsTimestampInUs;
+
+ // 2. Second request for the same access point (user approval bypass).
+ ScanResult matchingScanResult = mTestScanDatas[0].getResults()[0];
+ // simulate cache expiry
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(Long.valueOf(
+ scanResultsTimestampInUs / 1000
+ + WifiNetworkFactory.CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS + 1));
+ // Simulate the cached results matching.
+ when(mWifiScanner.getSingleScanResults())
+ .thenReturn(Arrays.asList(mTestScanDatas[0].getResults()));
+
+ PatternMatcher ssidPatternMatch =
+ new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+ Pair<MacAddress, MacAddress> bssidPatternMatch =
+ Pair.create(MacAddress.fromString(matchingScanResult.BSSID),
+ MacAddress.BROADCAST_ADDRESS);
+ WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+ ssidPatternMatch, bssidPatternMatch,
+ WifiConfigurationTestUtil.createPskNetwork(), TEST_UID_1, TEST_PACKAGE_NAME_1);
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ // Ensure we brought up the UI while the scan is ongoing.
+ validateUiStartParams(true);
+
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ // Trigger scan results & ensure we triggered a connect.
+ verify(mWifiScanner).startScan(any(), mScanListenerArgumentCaptor.capture(), any());
+ ScanListener scanListener = mScanListenerArgumentCaptor.getValue();
+ assertNotNull(scanListener);
+ scanListener.onResults(mTestScanDatas);
+
+ // Verify we did not trigger the match callback.
+ verify(mNetworkRequestMatchCallback, never()).onMatch(anyList());
+ // Verify that we sent a connection attempt to ClientModeImpl
+ verify(mClientModeImpl).sendMessage(any());
+
+ verify(mWifiMetrics).incrementNetworkRequestApiNumUserApprovalBypass();
+ }
+
+ /**
+ * Verify network specifier matching for a specifier containing a specific SSID match using
+ * 4 WPA_PSK scan results, each with unique SSID when the UI callback registration is delayed.
+ */
+ @Test
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchCallbackRegistrationDelayed()
+ throws Exception {
+ // 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,
+ TEST_PACKAGE_NAME_1);
+
+ mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+ mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+
+ validateUiStartParams(true);
+
+ verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+ // Ensure we did not send any match callbacks, until the callback is registered
+ verify(mNetworkRequestMatchCallback, never()).onMatch(any());
+
+ // Register the callback & ensure we triggered the on match callback.
+ mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+ TEST_CALLBACK_IDENTIFIER);
+ ArgumentCaptor<List<ScanResult>> matchedScanResultsCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mNetworkRequestMatchCallback).onMatch(matchedScanResultsCaptor.capture());
+
+ assertNotNull(matchedScanResultsCaptor.getValue());
+ // We only expect 1 network match in this case.
+ validateScanResults(matchedScanResultsCaptor.getValue(), mTestScanDatas[0].getResults()[0]);
+
+ verify(mWifiMetrics).incrementNetworkRequestApiMatchSizeHistogram(
+ matchedScanResultsCaptor.getValue().size());
+ }
+
private Messenger sendNetworkRequestAndSetupForConnectionStatus() throws RemoteException {
return sendNetworkRequestAndSetupForConnectionStatus(TEST_SSID_1);
}
@@ -2391,6 +2659,8 @@
mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+ validateUiStartParams(true);
+
mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
TEST_CALLBACK_IDENTIFIER);
verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
@@ -2398,7 +2668,7 @@
verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
- verify(mNetworkRequestMatchCallback).onMatch(anyList());
+ verify(mNetworkRequestMatchCallback, atLeastOnce()).onMatch(anyList());
}
// Simulates the periodic scans performed to find a matching network.
@@ -2413,6 +2683,10 @@
ScanListener scanListener = null;
mInOrder = inOrder(mWifiScanner, mAlarmManager);
+
+ // Before we start scans, ensure that we look at the latest cached scan results.
+ mInOrder.verify(mWifiScanner).getSingleScanResults();
+
for (int i = 0; i < expectedIntervalsInSeconds.length - 1; i++) {
long expectedCurrentIntervalInMs = expectedIntervalsInSeconds[i];
long expectedNextIntervalInMs = expectedIntervalsInSeconds[i + 1];
@@ -2590,7 +2864,7 @@
private void validateUiStartParams(boolean expectedIsReqForSingeNetwork) {
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).startActivityAsUser(
+ verify(mContext, atLeastOnce()).startActivityAsUser(
intentArgumentCaptor.capture(), eq(UserHandle.getUserHandleForUid(TEST_UID_1)));
Intent intent = intentArgumentCaptor.getValue();
assertNotNull(intent);
@@ -2621,4 +2895,61 @@
expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
}
+
+ /**
+ * Create a test scan data for target SSID list with specified number and encryption type
+ * @param scanResultType network encryption type
+ * @param nums Number of results with different BSSIDs for one SSID
+ * @param ssids target SSID list
+ */
+ private void setupScanDataSameSsidWithDiffBssid(int scanResultType, int nums, String[] ssids) {
+ String baseBssid = "11:34:56:78:90:";
+ int[] freq = new int[nums * ssids.length];
+ for (int i = 0; i < nums; i++) {
+ freq[i] = 2417 + i;
+ }
+ mTestScanDatas = ScanTestUtil.createScanDatas(new int[][]{ freq });
+ assertEquals(1, mTestScanDatas.length);
+ ScanResult[] scanResults = mTestScanDatas[0].getResults();
+ assertEquals(nums * ssids.length, scanResults.length);
+ String caps = getScanResultCapsForType(scanResultType);
+ for (int i = 0; i < ssids.length; i++) {
+ for (int j = i * nums; j < (i + 1) * nums; j++) {
+ scanResults[j].SSID = ssids[i];
+ scanResults[j].BSSID = baseBssid + Integer.toHexString(16 + j);
+ scanResults[j].capabilities = caps;
+ scanResults[j].level = -45;
+ }
+ }
+ }
+
+ /**
+ * Helper function for serializing configuration data to a XML block.
+ *
+ * @return byte[] of the XML data
+ * @throws Exception
+ */
+ private byte[] serializeData() throws Exception {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ mNetworkRequestStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
+ out.flush();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Helper function for parsing configuration data from a XML block.
+ *
+ * @param data XML data to parse from
+ * @throws Exception
+ */
+ private void deserializeData(byte[] data) throws Exception {
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ mNetworkRequestStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
index ed3e6d0..4dcee89 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
@@ -20,7 +20,7 @@
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OPSTR_CHANGE_WIFI_STATE;
import static android.app.AppOpsManager.OP_CHANGE_WIFI_STATE;
-import static android.app.Notification.EXTRA_TEXT;
+import static android.app.Notification.EXTRA_BIG_TEXT;
import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION;
import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION;
@@ -48,6 +48,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSuggestion;
+import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.UserHandle;
import android.os.test.TestLooper;
@@ -151,10 +152,12 @@
when(mContext.getApplicationInfo()).thenReturn(ourAppInfo);
// test app info
ApplicationInfo appInfO1 = new ApplicationInfo();
- when(mPackageManager.getApplicationInfo(TEST_PACKAGE_1, 0)).thenReturn(appInfO1);
+ when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_1), eq(0), anyInt()))
+ .thenReturn(appInfO1);
when(mPackageManager.getApplicationLabel(appInfO1)).thenReturn(TEST_APP_NAME_1);
ApplicationInfo appInfO2 = new ApplicationInfo();
- when(mPackageManager.getApplicationInfo(TEST_PACKAGE_2, 0)).thenReturn(appInfO2);
+ when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_2), eq(0), anyInt()))
+ .thenReturn(appInfO2);
when(mPackageManager.getApplicationLabel(appInfO2)).thenReturn(TEST_APP_NAME_2);
mWifiNetworkSuggestionsManager =
@@ -247,14 +250,16 @@
mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_1,
TEST_PACKAGE_2));
// Now remove all of them.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList1,
+ TEST_UID_1, TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_2));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList2,
+ TEST_UID_1, TEST_PACKAGE_2));
assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty());
@@ -296,9 +301,11 @@
// Now remove all of them by sending an empty list.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_1,
+ TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_PACKAGE_2));
+ mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_2,
+ TEST_PACKAGE_2));
assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty());
}
@@ -333,8 +340,8 @@
TEST_PACKAGE_1);
removingSuggestion.wifiConfiguration.SSID = networkSuggestion1.wifiConfiguration.SSID;
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(Arrays.asList(removingSuggestion),
- TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(new ArrayList<>(),
+ TEST_UID_1, TEST_PACKAGE_1));
verify(mWifiKeyStore).removeKeys(any());
}
/**
@@ -355,7 +362,8 @@
mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_UID_1,
+ TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
TEST_PACKAGE_1));
@@ -433,7 +441,8 @@
}
// The remove should succeed.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
// Now add 2 more.
networkSuggestionList = new ArrayList<>();
@@ -473,7 +482,8 @@
TEST_PACKAGE_1));
// Remove should fail because the network list is different.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_UID_1,
+ TEST_PACKAGE_1));
}
/**
@@ -802,7 +812,8 @@
// remove the suggestion & ensure lookup fails.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(Collections.EMPTY_LIST, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(Collections.EMPTY_LIST, TEST_UID_1,
+ TEST_PACKAGE_1));
assertNull(mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail));
}
@@ -864,7 +875,7 @@
validatePostConnectionBroadcastSent(TEST_PACKAGE_1, networkSuggestion);
// Verify no more broadcast were sent out.
- verifyNoMoreInteractions(mContext);
+ mInorder.verifyNoMoreInteractions();
}
/**
@@ -893,7 +904,11 @@
verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectFailure();
// Verify no more broadcast were sent out.
- verifyNoMoreInteractions(mContext);
+ mInorder.verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResults(
+ anyString(), anyInt());
+ mInorder.verify(mContext, never()).sendBroadcastAsUser(
+ any(), any());
+
}
/**
@@ -954,7 +969,7 @@
}
// Verify no more broadcast were sent out.
- verifyNoMoreInteractions(mContext);
+ mInorder.verifyNoMoreInteractions();
}
/**
@@ -1016,7 +1031,7 @@
}
// Verify no more broadcast were sent out.
- verifyNoMoreInteractions(mContext);
+ mInorder.verifyNoMoreInteractions();
}
/**
@@ -1079,7 +1094,7 @@
}
// Verify no more broadcast were sent out.
- verifyNoMoreInteractions(mContext);
+ mInorder.verifyNoMoreInteractions();
}
/**
@@ -1111,7 +1126,10 @@
TEST_BSSID);
// Verify no broadcast was sent out.
- verifyNoMoreInteractions(mContext, mWifiPermissionsUtil);
+ mInorder.verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResults(
+ anyString(), anyInt());
+ mInorder.verify(mContext, never()).sendBroadcastAsUser(
+ any(), any());
}
/**
@@ -1142,7 +1160,10 @@
TEST_BSSID);
// Verify no broadcast was sent out.
- verifyNoMoreInteractions(mContext, mWifiPermissionsUtil);
+ mInorder.verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResults(
+ anyString(), anyInt());
+ mInorder.verify(mContext, never()).sendBroadcastAsUser(
+ any(), any());
}
/**
@@ -1179,7 +1200,7 @@
.enforceCanAccessScanResults(TEST_PACKAGE_1, TEST_UID_1);
// Verify no broadcast was sent out.
- verifyNoMoreInteractions(mContext, mWifiPermissionsUtil);
+ mInorder.verifyNoMoreInteractions();
}
/**
@@ -1241,7 +1262,8 @@
mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
// Verify config store interactions.
verify(mWifiConfigManager, times(2)).saveToStore(true);
@@ -1350,10 +1372,11 @@
/**
* Verify that we don't disconnect from the network if the only network suggestion matching the
- * connected network is removed.
+ * connected network is removed when App doesn't have NetworkCarrierProvisioningPermission.
*/
@Test
- public void testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatch() {
+ public void
+ testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatchNoCarrierProvision() {
WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1,
TEST_PACKAGE_1);
@@ -1361,6 +1384,8 @@
new ArrayList<WifiNetworkSuggestion>() {{
add(networkSuggestion);
}};
+ when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1))
+ .thenReturn(false);
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
TEST_PACKAGE_1));
@@ -1373,11 +1398,79 @@
// Now remove the network suggestion and ensure we did not trigger a disconnect.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
verify(mClientModeImpl, never()).disconnectCommand();
}
/**
+ * Verify that we will disconnect from the network if the only network suggestion matching the
+ * connected network is removed when App has NetworkCarrierProvisioningPermission.
+ */
+ @Test
+ public void
+ testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatchCarrierProvision() {
+ WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ List<WifiNetworkSuggestion> networkSuggestionList =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(networkSuggestion);
+ }};
+ when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1))
+ .thenReturn(true);
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
+
+ // Simulate connecting to the network.
+ mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded(
+ WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration,
+ TEST_BSSID);
+
+ // Now remove the network suggestion and ensure we did trigger a disconnect.
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ verify(mClientModeImpl).disconnectCommand();
+ }
+
+ /**
+ * Verify that we will disconnect from network when App has NetworkCarrierProvisioningPermission
+ * and removed all its suggestions by remove empty list.
+ */
+ @Test
+ public void
+ testRemoveAllNetworkSuggestionsMatchingConnectionSuccessWithOneMatchCarrierProvision() {
+ WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ List<WifiNetworkSuggestion> networkSuggestionList =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(networkSuggestion);
+ }};
+ when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1))
+ .thenReturn(true);
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
+
+ // Simulate connecting to the network.
+ mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded(
+ WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration,
+ TEST_BSSID);
+
+ // Now remove all network suggestion and ensure we did trigger a disconnect.
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_1,
+ TEST_PACKAGE_1));
+ verify(mClientModeImpl).disconnectCommand();
+ }
+
+
+ /**
* Verify that we do not disconnect from the network if there are network suggestion from
* multiple apps matching the connected network when one of the apps is removed.
*/
@@ -1520,10 +1613,12 @@
// Now remove first add, nothing happens.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_UID_1,
+ TEST_PACKAGE_1));
// Stop watching app-ops changes on last remove.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_UID_1,
+ TEST_PACKAGE_1));
assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty());
mInorder.verify(mAppOpsManager).stopWatchingMode(mAppOpChangedListenerCaptor.getValue());
@@ -1702,9 +1797,11 @@
// Remove all suggestions from TEST_PACKAGE_1 & TEST_PACKAGE_2, we should continue to track.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_UID_1,
+ TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_2));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_UID_2,
+ TEST_PACKAGE_2));
assertTrue(mDataSource.hasNewDataToSerialize());
Map<String, PerAppInfo> networkSuggestionsMapToWrite = mDataSource.toSerialize();
@@ -1766,9 +1863,11 @@
// Remove all suggestions from TEST_PACKAGE_1 & TEST_PACKAGE_2, we should continue to track.
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_UID_1,
+ TEST_PACKAGE_1));
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
- mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_2));
+ mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_UID_2,
+ TEST_PACKAGE_2));
assertTrue(mDataSource.hasNewDataToSerialize());
Map<String, PerAppInfo> networkSuggestionsMapToWrite = mDataSource.toSerialize();
@@ -1791,10 +1890,11 @@
}
/**
- * Verify handling of user dismissal of the user approval notification.
+ * Verify user dismissal notification when first time add suggestions and dismissal the user
+ * approval notification when framework gets scan results.
*/
@Test
- public void testUserApprovalNotificationDismissal() {
+ public void testUserApprovalNotificationDismissalWhenGetScanResult() {
WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
TEST_PACKAGE_1);
@@ -1805,6 +1905,11 @@
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
TEST_PACKAGE_1));
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+ // Simulate user dismissal notification.
+ sendBroadcastForUserAction(
+ NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
+ reset(mNotificationManger);
// Simulate finding the network in scan results.
mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1826,10 +1931,11 @@
}
/**
- * Verify handling of user clicking allow on the user approval notification.
+ * Verify user dismissal notification when first time add suggestions and click on allow on
+ * the user approval notification when framework gets scan results.
*/
@Test
- public void testUserApprovalNotificationClickOnAllow() {
+ public void testUserApprovalNotificationClickOnAllowWhenGetScanResult() {
WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
TEST_PACKAGE_1);
@@ -1840,6 +1946,12 @@
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
TEST_PACKAGE_1));
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+
+ // Simulate user dismissal notification.
+ sendBroadcastForUserAction(
+ NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
+ reset(mNotificationManger);
// Simulate finding the network in scan results.
mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1865,10 +1977,11 @@
}
/**
- * Verify handling of user clicking disallow on the user approval notification.
+ * Verify user dismissal notification when first time add suggestions and click on disallow on
+ * the user approval notification when framework gets scan results.
*/
@Test
- public void testUserApprovalNotificationClickOnDisallow() {
+ public void testUserApprovalNotificationClickOnDisallowWhenGetScanResult() {
WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
TEST_PACKAGE_1);
@@ -1881,6 +1994,12 @@
TEST_PACKAGE_1));
verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE),
eq(TEST_PACKAGE_1), mAppOpChangedListenerCaptor.capture());
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+
+ // Simulate user dismissal notification.
+ sendBroadcastForUserAction(
+ NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
+ reset(mNotificationManger);
// Simulate finding the network in scan results.
mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1959,6 +2078,134 @@
}
/**
+ * Verify get hidden networks from All user approve network suggestions
+ */
+ @Test
+ public void testGetHiddenNetworks() {
+
+ WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ WifiNetworkSuggestion hiddenNetworkSuggestion1 = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createPskHiddenNetwork(), true, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ WifiNetworkSuggestion hiddenNetworkSuggestion2 = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createPskHiddenNetwork(), true, false, TEST_UID_2,
+ TEST_PACKAGE_2);
+ List<WifiNetworkSuggestion> networkSuggestionList1 =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(networkSuggestion);
+ add(hiddenNetworkSuggestion1);
+ }};
+ List<WifiNetworkSuggestion> networkSuggestionList2 =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(hiddenNetworkSuggestion2);
+ }};
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+ TEST_PACKAGE_1));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+ TEST_PACKAGE_2));
+ mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
+ mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(false, TEST_PACKAGE_2);
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
+ mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList();
+ assertEquals(1, hiddenNetworks.size());
+ assertEquals(hiddenNetworkSuggestion1.wifiConfiguration.SSID, hiddenNetworks.get(0).ssid);
+ }
+
+ /**
+ * Verify handling of user clicking allow on the user approval notification when first time
+ * add suggestions.
+ */
+ @Test
+ public void testUserApprovalNotificationClickOnAllowDuringAddingSuggestions() {
+ WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ List<WifiNetworkSuggestion> networkSuggestionList =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(networkSuggestion);
+ }};
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+
+ // Simulate user clicking on allow in the notification.
+ sendBroadcastForUserAction(
+ NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
+ // Cancel the notification.
+ verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE);
+
+ // Verify config store interactions.
+ verify(mWifiConfigManager, times(2)).saveToStore(true);
+ assertTrue(mDataSource.hasNewDataToSerialize());
+
+ reset(mNotificationManger);
+ // We should not resend the notification next time the network is found in scan results.
+ mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
+ createScanDetailForNetwork(networkSuggestion.wifiConfiguration));
+ verifyNoMoreInteractions(mNotificationManger);
+ }
+
+ /**
+ * Verify handling of user clicking Disallow on the user approval notification when first time
+ * add suggestions.
+ */
+ @Test
+ public void testUserApprovalNotificationClickOnDisallowWhenAddSuggestions() {
+ WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+ WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1,
+ TEST_PACKAGE_1);
+ List<WifiNetworkSuggestion> networkSuggestionList =
+ new ArrayList<WifiNetworkSuggestion>() {{
+ add(networkSuggestion);
+ }};
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE),
+ eq(TEST_PACKAGE_1), mAppOpChangedListenerCaptor.capture());
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+
+ // Simulate user clicking on disallow in the notification.
+ sendBroadcastForUserAction(
+ NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
+ // Ensure we turn off CHANGE_WIFI_STATE app-ops.
+ verify(mAppOpsManager).setMode(
+ OP_CHANGE_WIFI_STATE, TEST_UID_1,
+ TEST_PACKAGE_1, MODE_IGNORED);
+ // Cancel the notification.
+ verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE);
+
+ // Verify config store interactions.
+ verify(mWifiConfigManager, times(2)).saveToStore(true);
+ assertTrue(mDataSource.hasNewDataToSerialize());
+
+ reset(mNotificationManger);
+
+ // Now trigger the app-ops callback to ensure we remove all of their suggestions.
+ AppOpsManager.OnOpChangedListener listener = mAppOpChangedListenerCaptor.getValue();
+ assertNotNull(listener);
+ when(mAppOpsManager.unsafeCheckOpNoThrow(
+ OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+ TEST_PACKAGE_1))
+ .thenReturn(MODE_IGNORED);
+ listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
+ mLooper.dispatchAll();
+ assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty());
+
+ // Assuming the user re-enabled the app again & added the same suggestions back.
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+ TEST_PACKAGE_1));
+ validateUserApprovalNotification(TEST_APP_NAME_1);
+ verifyNoMoreInteractions(mNotificationManger);
+ }
+
+ /**
* Creates a scan detail corresponding to the provided network values.
*/
private ScanDetail createScanDetailForNetwork(WifiConfiguration configuration) {
@@ -1987,7 +2234,7 @@
private boolean checkUserApprovalNotificationParams(
Notification notification, String expectedAppName) {
- if (!notification.extras.getString(EXTRA_TEXT).contains(expectedAppName)) return false;
+ if (!notification.extras.getString(EXTRA_BIG_TEXT).contains(expectedAppName)) return false;
return true;
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index b69fba7..d5f7dd9 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -174,6 +174,7 @@
private static final String WIFI_IFACE_NAME2 = "wlan1";
private static final String TEST_COUNTRY_CODE = "US";
private static final String TEST_FACTORY_MAC = "10:22:34:56:78:92";
+ private static final String TEST_FQDN = "testfqdn";
private static final List<WifiConfiguration> TEST_WIFI_CONFIGURATION_LIST = Arrays.asList(
WifiConfigurationTestUtil.generateWifiConfig(
0, 1000000, "\"red\"", true, true, null, null),
@@ -349,7 +350,8 @@
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getApplicationInfo(any(), anyInt())).thenReturn(mApplicationInfo);
+ when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(mApplicationInfo);
when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(),
anyBoolean(), any());
@@ -544,7 +546,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(anyBoolean())).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -563,7 +565,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(false);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -600,6 +602,44 @@
}
/**
+ * Verify that wifi can be enabled by the DO apps targeting Q SDK.
+ */
+ @Test
+ public void testSetWifiEnabledSuccessForDOAppsTargetingQSDK() throws Exception {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(
+ Process.myUid(), DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
+ .thenReturn(true);
+
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that wifi can be enabled by the system apps targeting Q SDK.
+ */
+ @Test
+ public void testSetWifiEnabledSuccessForSystemAppsTargetingQSDK() throws Exception {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
* Verify that wifi can be enabled by the apps targeting pre-Q SDK.
*/
@Test
@@ -607,7 +647,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -617,6 +657,20 @@
}
/**
+ * Verify that wifi is not enabled when wificontroller is not started.
+ */
+ @Test
+ public void testSetWifiEnabledFailureWhenInCryptDebounce() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+ when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+ verifyZeroInteractions(mWifiController);
+ }
+
+ /**
* Verify that wifi cannot be enabled by the apps targeting Q SDK.
*/
@Test
@@ -624,7 +678,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(false);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -641,7 +695,7 @@
doThrow(new SecurityException()).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
try {
mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
@@ -661,7 +715,7 @@
doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
@@ -693,7 +747,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
when(mContext.checkPermission(
@@ -737,7 +791,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
mWifiServiceImpl.checkAndStartWifi();
@@ -812,6 +866,45 @@
}
/**
+ * Verify that wifi can be disabled by the PO apps targeting Q SDK.
+ */
+ @Test
+ public void testSetWifiDisabledSuccessForPOAppsTargetingQSDK() throws Exception {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(
+ Process.myUid(), DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
+ .thenReturn(true);
+
+ when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
+
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that wifi can be disabled by the system apps targeting Q SDK.
+ */
+ @Test
+ public void testSetWifiDisabledSuccessForSystemAppsTargetingQSDK() throws Exception {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
+
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+
+ /**
* Verify that wifi can be disabled by the apps targeting pre-Q SDK.
*/
@Test
@@ -819,7 +912,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -829,6 +922,20 @@
}
/**
+ * Verify that wifi is not disabled when wificontroller is not started.
+ */
+ @Test
+ public void testSetWifiDisabledFailureWhenInCryptDebounce() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+ when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+ assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
+ verifyZeroInteractions(mWifiController);
+ }
+
+ /**
* Verify that wifi cannot be disabled by the apps targeting Q SDK.
*/
@Test
@@ -836,7 +943,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(false);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false);
when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
@@ -1085,6 +1192,19 @@
}
/**
+ * Verify does not start softap when wificontroller is not started.
+ */
+ @Test
+ public void testStartSoftApWhenInCryptDebounce() {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+
+ WifiConfiguration config = createValidSoftApConfiguration();
+ boolean result = mWifiServiceImpl.startSoftAp(config);
+ assertFalse(result);
+ verifyZeroInteractions(mWifiController);
+ }
+
+ /**
* Verify a SecurityException is thrown when a caller without the correct permission attempts to
* start softap.
*/
@@ -1108,6 +1228,18 @@
}
/**
+ * Verify does not stop softap when wificontroller is not started.
+ */
+ @Test
+ public void testStopSoftApWhenInCryptDebounce() {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+
+ boolean result = mWifiServiceImpl.stopSoftAp();
+ assertFalse(result);
+ verifyZeroInteractions(mWifiController);
+ }
+
+ /**
* Verify SecurityException is thrown when a caller without the correct permission attempts to
* stop softap.
*/
@@ -1462,6 +1594,19 @@
}
/**
+ * Only start LocalOnlyHotspot if device is in crypt debounce mode.
+ */
+ @Test
+ public void testStartLocalOnlyHotspotFailsIfInCryptDebounce() throws Exception {
+ when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true);
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder,
+ TEST_PACKAGE_NAME);
+ assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result);
+ }
+
+ /**
* Only start LocalOnlyHotspot if we are not tethering.
*/
@Test
@@ -1479,7 +1624,6 @@
// Start another session without a stop, that should fail.
assertFalse(mWifiServiceImpl.startSoftAp(createValidSoftApConfiguration()));
-
verifyNoMoreInteractions(mWifiController);
}
@@ -2516,9 +2660,9 @@
PackageManager pm = mock(PackageManager.class);
when(pm.hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true);
when(mContext.getPackageManager()).thenReturn(pm);
- when(pm.getApplicationInfo(any(), anyInt())).thenReturn(mApplicationInfo);
+ when(pm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(mApplicationInfo);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
when(mClientModeImpl.syncAddOrUpdatePasspointConfig(any(),
any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME))).thenReturn(
@@ -2645,77 +2789,57 @@
}
/**
- * Verify that the call to getPasspointConfigurations is not redirected to specific API
- * syncGetPasspointConfigs when the caller doesn't have NETWORK_SETTINGS permissions and
- * NETWORK_SETUP_WIZARD.
- */
- @Test(expected = SecurityException.class)
- public void testGetPasspointConfigurationsWithOutPermissions() {
- when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
- when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false);
-
- mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME);
- }
-
- /**
- * Verify that getPasspointConfigurations called by apps that has invalid package will
- * throw {@link SecurityException}.
- */
- @Test(expected = SecurityException.class)
- public void testGetPasspointConfigurationWithInvalidPackage() {
- doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(),
- eq(TEST_PACKAGE_NAME));
- when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
- when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(true);
-
- mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME);
- }
-
- /**
- * Verify that getPasspointConfigurations called by apps targeting below Q SDK will return
- * empty list if the caller doesn't have NETWORK_SETTINGS permissions and NETWORK_SETUP_WIZARD.
+ * Verify the call to getPasspointConfigurations when the caller doesn't have
+ * NETWORK_SETTINGS and NETWORK_SETUP_WIZARD permissions.
*/
@Test
- public void testGetPasspointConfigurationForAppsTargetingBelowQSDK() {
+ public void testGetPasspointConfigurationsWithOutPrivilegedPermissions() {
when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false);
- when(mWifiPermissionsUtil.isTargetSdkLessThan(eq(TEST_PACKAGE_NAME),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
- List<PasspointConfiguration> result = mWifiServiceImpl.getPasspointConfigurations(
- TEST_PACKAGE_NAME);
- assertNotNull(result);
- assertEquals(0, result.size());
+ mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME);
+
+ verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(false));
}
/**
- * Verify that the call to removePasspointConfiguration is not redirected to specific API
- * syncRemovePasspointConfig when the caller doesn't have NETWORK_SETTINGS and
+ * Verify that the call to getPasspointConfigurations when the caller does have
+ * NETWORK_SETTINGS permission.
+ */
+ @Test
+ public void testGetPasspointConfigurationsWithPrivilegedPermissions() {
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+
+ mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME);
+
+ verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(true));
+ }
+
+ /**
+ * Verify the call to removePasspointConfigurations when the caller doesn't have
+ * NETWORK_SETTINGS and NETWORK_CARRIER_PROVISIONING permissions.
+ */
+ @Test
+ public void testRemovePasspointConfigurationWithOutPrivilegedPermissions() {
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn(
+ false);
+
+ mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME);
+ verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(false), eq(TEST_FQDN));
+ }
+
+ /**
+ * Verify the call to removePasspointConfigurations when the caller does have
* NETWORK_CARRIER_PROVISIONING permission.
*/
- @Test(expected = SecurityException.class)
- public void testRemovePasspointConfigurationWithOutPermissions() {
- when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
- when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn(
- false);
-
- mWifiServiceImpl.removePasspointConfiguration(null, null);
- }
-
- /**
- * Verify that the call to removePasspointConfiguration for apps targeting below Q SDK will
- * return false if the caller doesn't have NETWORK_SETTINGS and NETWORK_CARRIER_PROVISIONING
- * permission.
- */
@Test
- public void testRemovePasspointConfigurationForAppsTargetingBelowQSDK() {
- when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+ public void testRemovePasspointConfigurationWithPrivilegedPermissions() {
when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn(
- false);
- when(mWifiPermissionsUtil.isTargetSdkLessThan(isNull(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ true);
- assertFalse(mWifiServiceImpl.removePasspointConfiguration(null, null));
+ mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME);
+ verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(true), eq(TEST_FQDN));
}
/**
@@ -3306,6 +3430,25 @@
}
/**
+ * Verifies that entering airplane mode does not reset country code.
+ */
+ @Test
+ public void testEnterAirplaneModeNotResetCountryCode() {
+ mWifiServiceImpl.checkAndStartWifi();
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat((IntentFilter filter) ->
+ filter.hasAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)));
+
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+
+ // Send the broadcast
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent);
+
+ verifyNoMoreInteractions(mWifiCountryCode);
+ }
+
+ /**
* Verify calls to notify users of a softap config change check the NETWORK_SETTINGS permission.
*/
@Test
@@ -3507,13 +3650,14 @@
mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel;
when(mClientModeImpl.syncGetConfiguredNetworks(anyInt(), any(), anyInt()))
.thenReturn(Arrays.asList(network));
- when(mClientModeImpl.syncGetPasspointConfigs(any())).thenReturn(Arrays.asList(config));
+ when(mClientModeImpl.syncGetPasspointConfigs(any(), anyBoolean()))
+ .thenReturn(Arrays.asList(config));
mWifiServiceImpl.factoryReset(TEST_PACKAGE_NAME);
mLooper.dispatchAll();
verify(mClientModeImpl).syncRemoveNetwork(mAsyncChannel, network.networkId);
- verify(mClientModeImpl).syncRemovePasspointConfig(mAsyncChannel, fqdn);
+ verify(mClientModeImpl).syncRemovePasspointConfig(mAsyncChannel, true, fqdn);
verify(mWifiConfigManager).clearDeletedEphemeralNetworks();
verify(mClientModeImpl).clearNetworkRequestUserApprovedAccessPoints();
verify(mWifiNetworkSuggestionsManager).clear();
@@ -3536,8 +3680,9 @@
mLooper.dispatchAll();
verify(mClientModeImpl).syncGetConfiguredNetworks(anyInt(), any(), anyInt());
- verify(mClientModeImpl, never()).syncGetPasspointConfigs(any());
- verify(mClientModeImpl, never()).syncRemovePasspointConfig(any(), anyString());
+ verify(mClientModeImpl, never()).syncGetPasspointConfigs(any(), anyBoolean());
+ verify(mClientModeImpl, never()).syncRemovePasspointConfig(
+ any(), anyBoolean(), anyString());
verify(mWifiConfigManager).clearDeletedEphemeralNetworks();
verify(mClientModeImpl).clearNetworkRequestUserApprovedAccessPoints();
verify(mWifiNetworkSuggestionsManager).clear();
@@ -3560,7 +3705,7 @@
} catch (SecurityException e) {
}
verify(mClientModeImpl, never()).syncGetConfiguredNetworks(anyInt(), any(), anyInt());
- verify(mClientModeImpl, never()).syncGetPasspointConfigs(any());
+ verify(mClientModeImpl, never()).syncGetPasspointConfigs(any(), eq(false));
}
/**
@@ -3591,7 +3736,7 @@
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME));
@@ -3734,7 +3879,7 @@
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
.noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
- eq(Build.VERSION_CODES.Q))).thenReturn(true);
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME);
@@ -3793,12 +3938,12 @@
public void testRemoveNetworkSuggestions() {
setupClientModeImplHandlerForRunWithScissors();
- when(mWifiNetworkSuggestionsManager.remove(any(), anyString()))
+ when(mWifiNetworkSuggestionsManager.remove(any(), anyInt(), anyString()))
.thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID);
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
- when(mWifiNetworkSuggestionsManager.remove(any(), anyString()))
+ when(mWifiNetworkSuggestionsManager.remove(any(), anyInt(), anyString()))
.thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
@@ -3808,7 +3953,8 @@
assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
- verify(mWifiNetworkSuggestionsManager, times(2)).remove(any(), eq(TEST_PACKAGE_NAME));
+ verify(mWifiNetworkSuggestionsManager, times(2)).remove(any(), anyInt(),
+ eq(TEST_PACKAGE_NAME));
}
/**
@@ -4125,4 +4271,17 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Test to verify that the lock mode is verified before dispatching the operation
+ *
+ * Steps: call acquireWifiLock with an invalid lock mode.
+ * Expected: the call should throw an IllegalArgumentException.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void acquireWifiLockShouldThrowExceptionOnInvalidLockMode() throws Exception {
+ final int wifiLockModeInvalid = -1;
+
+ mWifiServiceImpl.acquireWifiLock(mAppBinder, wifiLockModeInvalid, "", null);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 5a95b95..c33a4d5 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -730,7 +730,7 @@
IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN
| IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
);
- int expected = (
+ long expected = (
WifiManager.WIFI_FEATURE_SCANNER
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
assertEquals(expected, mWifiVendorHal.wifiFeatureMaskFromStaCapabilities(caps));
@@ -749,7 +749,7 @@
| android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT
| android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2AP_RTT
);
- int expected = (
+ long expected = (
WifiManager.WIFI_FEATURE_TX_POWER_LIMIT
| WifiManager.WIFI_FEATURE_D2D_RTT
| WifiManager.WIFI_FEATURE_D2AP_RTT
@@ -768,7 +768,7 @@
android.hardware.wifi.V1_3.IWifiChip.ChipCapabilityMask.SET_LATENCY_MODE
| android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT
);
- int expected = (
+ long expected = (
WifiManager.WIFI_FEATURE_LOW_LATENCY
| WifiManager.WIFI_FEATURE_D2D_RTT
);
@@ -794,7 +794,7 @@
add(IfaceType.STA);
add(IfaceType.P2P);
}};
- int expectedFeatureSet = (
+ long expectedFeatureSet = (
WifiManager.WIFI_FEATURE_SCANNER
| WifiManager.WIFI_FEATURE_LINK_LAYER_STATS
| WifiManager.WIFI_FEATURE_TX_POWER_LIMIT
@@ -3110,6 +3110,7 @@
radioModeInfos.add(radioModeInfo1);
mIWifiChipEventCallbackV12.onRadioModeChange(radioModeInfos);
+ mLooper.dispatchAll();
verify(mVendorHalRadioModeChangeHandler).onDbs();
verifyNoMoreInteractions(mVendorHalRadioModeChangeHandler);
@@ -3142,6 +3143,7 @@
radioModeInfos.add(radioModeInfo1);
mIWifiChipEventCallbackV12.onRadioModeChange(radioModeInfos);
+ mLooper.dispatchAll();
verify(mVendorHalRadioModeChangeHandler).onSbs(WifiScanner.WIFI_BAND_5_GHZ);
verifyNoMoreInteractions(mVendorHalRadioModeChangeHandler);
@@ -3171,6 +3173,7 @@
radioModeInfos.add(radioModeInfo0);
mIWifiChipEventCallbackV12.onRadioModeChange(radioModeInfos);
+ mLooper.dispatchAll();
verify(mVendorHalRadioModeChangeHandler).onScc(WifiScanner.WIFI_BAND_5_GHZ);
verifyNoMoreInteractions(mVendorHalRadioModeChangeHandler);
@@ -3200,6 +3203,7 @@
radioModeInfos.add(radioModeInfo0);
mIWifiChipEventCallbackV12.onRadioModeChange(radioModeInfos);
+ mLooper.dispatchAll();
verify(mVendorHalRadioModeChangeHandler).onMcc(WifiScanner.WIFI_BAND_BOTH);
verifyNoMoreInteractions(mVendorHalRadioModeChangeHandler);
@@ -3230,6 +3234,7 @@
radioModeInfos.add(radioModeInfo1);
mIWifiChipEventCallbackV12.onRadioModeChange(radioModeInfos);
+ mLooper.dispatchAll();
// Ignored....
verifyNoMoreInteractions(mVendorHalRadioModeChangeHandler);
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index f2cc45f..35916d2 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -161,7 +161,8 @@
// by default pretend to be an old API: i.e. allow Responders configured as *ANY*. This
// allows older (more extrensive) tests to run.
- when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt())).thenReturn(true);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt()))
+ .thenReturn(true);
when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true);
mDut = new WifiAwareStateManager();
@@ -1017,7 +1018,8 @@
*/
@Test
public void testDataPathResonderMacPassphraseNoPeerIdSuccessNonLegacy() throws Exception {
- when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt()))
+ .thenReturn(false);
testDataPathResponderUtility(false, false, false, true, true);
}
@@ -1028,7 +1030,8 @@
@Test
public void testDataPathResonderMacOpenNoPeerIdNoPmkPassphraseSuccessNonLegacy()
throws Exception {
- when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt()))
+ .thenReturn(false);
testDataPathResponderUtility(false, false, false, false, true);
}
@@ -1074,7 +1077,8 @@
*/
@Test
public void testDataPathResonderDirectNoMacPassphraseSuccessNonLegacy() throws Exception {
- when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt()))
+ .thenReturn(false);
testDataPathResponderUtility(true, false, false, true, true);
}
@@ -1084,7 +1088,8 @@
*/
@Test
public void testDataPathResonderDirectNoMacNoPmkPassphraseSuccessNonLegacy() throws Exception {
- when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt()))
+ .thenReturn(false);
testDataPathResponderUtility(true, false, false, false, true);
}
@@ -1473,7 +1478,7 @@
InOrder inOrderM = inOrder(mAwareMetricsMock);
boolean isLegacy = mWifiPermissionsUtil.isTargetSdkLessThan("anything",
- Build.VERSION_CODES.P);
+ Build.VERSION_CODES.P, 0);
if (providePmk) {
when(mPermissionsWrapperMock.getUidPermission(
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
index 4d4ea44..df9c332 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
@@ -33,7 +33,6 @@
import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
-import com.android.server.wifi.hotspot2.anqp.eap.InnerAuthEAP;
import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
import org.junit.Test;
@@ -102,7 +101,7 @@
*/
@Test
public void matchRoamingConsortiumWithNullElement() throws Exception {
- assertFalse(ANQPMatcher.matchRoamingConsortium(null, new long[0]));
+ assertFalse(ANQPMatcher.matchRoamingConsortium(null, new long[0], false));
}
/**
@@ -116,23 +115,22 @@
long oi = 0x1234L;
RoamingConsortiumElement element =
new RoamingConsortiumElement(Arrays.asList(new Long[] {oi}));
- assertTrue(ANQPMatcher.matchRoamingConsortium(element, new long[] {oi}));
+ assertTrue(ANQPMatcher.matchRoamingConsortium(element, new long[] {oi}, false));
}
/**
- * Verify that an indeterminate match will be returned when matching a null NAI Realm
+ * Verify that no match will be returned when matching a null NAI Realm
* ANQP element.
*
* @throws Exception
*/
@Test
public void matchNAIRealmWithNullElement() throws Exception {
- assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(null, "test.com",
- EAPConstants.EAP_TLS, new InnerAuthEAP(EAPConstants.EAP_TTLS)));
+ assertFalse(ANQPMatcher.matchNAIRealm(null, "test.com"));
}
/**
- * Verify that an indeterminate match will be returned when matching a NAI Realm
+ * Verify that no match will be returned when matching a NAI Realm
* ANQP element contained no NAI realm data.
*
* @throws Exception
@@ -140,8 +138,7 @@
@Test
public void matchNAIRealmWithEmtpyRealmData() throws Exception {
NAIRealmElement element = new NAIRealmElement(new ArrayList<NAIRealmData>());
- assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(element, "test.com",
- EAPConstants.EAP_TLS, null));
+ assertFalse(ANQPMatcher.matchNAIRealm(element, "test.com"));
}
/**
@@ -157,38 +154,11 @@
Arrays.asList(new String[] {realm}), new ArrayList<EAPMethod>());
NAIRealmElement element = new NAIRealmElement(
Arrays.asList(new NAIRealmData[] {realmData}));
- assertEquals(AuthMatch.REALM, ANQPMatcher.matchNAIRealm(element, realm,
- EAPConstants.EAP_TLS, null));
+ assertTrue(ANQPMatcher.matchNAIRealm(element, realm));
}
/**
- * Verify that method match will be returned when the specified EAP
- * method only matches a eap method in the NAI Realm ANQP element if the element does not have
- * auth params.
- *
- * @throws Exception
- */
- @Test
- public void matchNAIRealmWithMethodMatch() throws Exception {
- // Test data.
- String providerRealm = "test.com";
- String anqpRealm = "test2.com";
- NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
- int eapMethodID = EAPConstants.EAP_TLS;
-
- // Setup NAI Realm element that has EAP method and no auth params.
- EAPMethod method = new EAPMethod(eapMethodID, new HashMap<Integer, Set<AuthParam>>());
- NAIRealmData realmData = new NAIRealmData(
- Arrays.asList(new String[]{anqpRealm}), Arrays.asList(new EAPMethod[]{method}));
- NAIRealmElement element = new NAIRealmElement(
- Arrays.asList(new NAIRealmData[]{realmData}));
-
- assertEquals(AuthMatch.METHOD,
- ANQPMatcher.matchNAIRealm(element, providerRealm, eapMethodID, authParam));
- }
-
- /**
- * Verify that a realm and method match will be returned when the specified realm and EAP
+ * Verify that a realm match will be returned when the specified realm and EAP
* method matches a realm in the NAI Realm ANQP element.
*
* @throws Exception
@@ -206,12 +176,11 @@
NAIRealmElement element = new NAIRealmElement(
Arrays.asList(new NAIRealmData[] {realmData}));
- assertEquals(AuthMatch.REALM | AuthMatch.METHOD,
- ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, null));
+ assertTrue(ANQPMatcher.matchNAIRealm(element, realm));
}
/**
- * Verify that an exact match will be returned when the specified realm, EAP
+ * Verify that a realm match will be returned when the specified realm, EAP
* method, and the authentication parameter matches a realm with the associated EAP method and
* authentication parameter in the NAI Realm ANQP element.
*
@@ -235,12 +204,11 @@
NAIRealmElement element = new NAIRealmElement(
Arrays.asList(new NAIRealmData[] {realmData}));
- assertEquals(AuthMatch.EXACT,
- ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, authParam));
+ assertTrue(ANQPMatcher.matchNAIRealm(element, realm));
}
/**
- * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified EAP method
+ * Verify that a realm match will be returned when the specified EAP method
* doesn't match with the corresponding EAP method in the NAI Realm ANQP element.
*
* @throws Exception
@@ -263,12 +231,11 @@
NAIRealmElement element = new NAIRealmElement(
Arrays.asList(new NAIRealmData[] {realmData}));
- assertEquals(AuthMatch.NONE,
- ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TLS, null));
+ assertTrue(ANQPMatcher.matchNAIRealm(element, realm));
}
/**
- * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified authentication
+ * Verify that a realm match will be returned when the specified authentication
* parameter doesn't match with the corresponding authentication parameter in the NAI Realm
* ANQP element.
*
@@ -292,10 +259,8 @@
NAIRealmElement element = new NAIRealmElement(
Arrays.asList(new NAIRealmData[] {realmData}));
- // Mismatch in authentication type.
- assertEquals(AuthMatch.NONE,
- ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TTLS,
- new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_PAP)));
+ // Mismatch in authentication type which we ignore.
+ assertTrue(ANQPMatcher.matchNAIRealm(element, realm));
}
/**
@@ -458,4 +423,64 @@
assertEquals(-1,
ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element));
}
+
+ /**
+ * Verify that match is found when HomeOI contains some of the RCOIs advertised by an AP marked
+ * as not required.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAnyHomeOi() throws Exception {
+ long[] providerOis = new long[] {0x1234L, 0x5678L, 0xabcdL};
+ Long[] anqpOis = new Long[] {0x1234L, 0x5678L, 0xdeadL, 0xf0cdL};
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(anqpOis));
+ assertTrue(ANQPMatcher.matchRoamingConsortium(element, providerOis, false));
+ }
+
+ /**
+ * Verify that no match is found when HomeOI does not contain any of the RCOIs advertised by an
+ * AP marked as not required.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAnyHomeOiNegative() throws Exception {
+ long[] providerOis = new long[] {0x1234L, 0x5678L, 0xabcdL};
+ Long[] anqpOis = new Long[] {0xabc2L, 0x1232L};
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(anqpOis));
+ assertFalse(ANQPMatcher.matchRoamingConsortium(element, providerOis, false));
+ }
+
+ /**
+ * Verify that match is found when HomeOI contains all of the RCOIs advertised by an AP marked
+ * as required.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAllHomeOi() throws Exception {
+ long[] providerOis = new long[] {0x1234L, 0x5678L, 0xabcdL};
+ Long[] anqpOis = new Long[] {0x1234L, 0x5678L, 0xabcdL, 0xdeadL, 0xf0cdL};
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(anqpOis));
+ assertTrue(ANQPMatcher.matchRoamingConsortium(element, providerOis, true));
+ }
+
+ /**
+ * Verify that match is not found when HomeOI does not contain all of the RCOIs advertised by an
+ * AP marked as required.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAllHomeOiNegative() throws Exception {
+ long[] providerOis = new long[] {0x1234L, 0x5678L, 0xabcdL};
+ Long[] anqpOis = new Long[] {0x1234L, 0x5678L, 0xdeadL, 0xf0cdL};
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(anqpOis));
+ assertFalse(ANQPMatcher.matchRoamingConsortium(element, providerOis, true));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java
index c5baac7..9fa92c9 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java
@@ -81,20 +81,21 @@
*/
@SmallTest
public class OsuServerConnectionTest {
- private static final String TEST_VALID_URL = "http://www.google.com";
+ private static final String TEST_VALID_URL = "https://www.google.com";
+ private static final String TEST_INVALID_URL = "http://www.google.com";
private static final String AUTH_TYPE = "ECDHE_RSA";
private static final String PROVIDER_NAME_VALID = "Boingo";
private static final String PROVIDER_NAME_INVALID = "Boingo1";
+ private static final String TEST_PROVIDER_CHINESE_NAME = "宝音阁";
private static final int ENABLE_VERBOSE_LOGGING = 1;
private static final int TEST_SESSION_ID = 1;
private TestLooper mLooper = new TestLooper();
private OsuServerConnection mOsuServerConnection;
- private URL mValidServerUrl;
+ private URL mServerUrl;
private List<Pair<Locale, String>> mProviderIdentities = new ArrayList<>();
private ArgumentCaptor<TrustManager[]> mTrustManagerCaptor =
ArgumentCaptor.forClass(TrustManager[].class);
-
private Map<Integer, Map<String, byte[]>> mTrustCertsInfo = new HashMap<>();
@Mock PasspointProvisioner.OsuServerCallbacks mOsuServerCallbacks;
@@ -114,7 +115,7 @@
mOsuServerConnection = new OsuServerConnection(mLooper.getLooper());
mOsuServerConnection.enableVerboseLogging(ENABLE_VERBOSE_LOGGING);
mProviderIdentities.add(Pair.create(Locale.US, PROVIDER_NAME_VALID));
- mValidServerUrl = new URL(TEST_VALID_URL);
+ mServerUrl = new URL(TEST_VALID_URL);
when(mWfaKeyStore.get()).thenReturn(mKeyStore);
when(mOsuServerCallbacks.getSessionId()).thenReturn(TEST_SESSION_ID);
when(mNetwork.openConnection(any(URL.class))).thenReturn(mUrlConnection);
@@ -144,7 +145,85 @@
trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
verify(mOsuServerCallbacks).onServerValidationStatus(anyInt(), eq(true));
- assertTrue(mOsuServerConnection.validateProvider(Locale.US, PROVIDER_NAME_VALID));
+ Map<String, String> providerNames = new HashMap<>();
+ providerNames.put(Locale.US.getISO3Language(), PROVIDER_NAME_VALID);
+ assertTrue(mOsuServerConnection.validateProvider(providerNames));
+ } finally {
+ session.finishMocking();
+ }
+ }
+
+ /**
+ * Verifies multiple languages of OsuProvider names are matched with cert
+ */
+ @Test
+ public void verifyValidateProviderWithMultipleProviderLangs() throws Exception {
+ // static mocking
+ MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(
+ ServiceProviderVerifier.class).startMocking();
+ try {
+ when(ServiceProviderVerifier.getProviderNames(any(X509Certificate.class))).thenReturn(
+ mProviderIdentities);
+ establishServerConnection();
+ TrustManager[] trustManagers = mTrustManagerCaptor.getValue();
+ X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+ trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
+ Map<String, String> friendlyNames = new HashMap<>();
+ friendlyNames.put(
+ Locale.SIMPLIFIED_CHINESE.getISO3Language(), TEST_PROVIDER_CHINESE_NAME);
+ friendlyNames.put(Locale.US.getISO3Language(), PROVIDER_NAME_VALID);
+
+ assertTrue(mOsuServerConnection.validateProvider(friendlyNames));
+ } finally {
+ session.finishMocking();
+ }
+ }
+
+ /**
+ * Verifies wrong language of OsuProvider name is mismatched with cert
+ */
+ @Test
+ public void verifyValidateProviderWithMismatchedProviderLang() throws Exception {
+ // static mocking
+ MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(
+ ServiceProviderVerifier.class).startMocking();
+ try {
+ when(ServiceProviderVerifier.getProviderNames(any(X509Certificate.class))).thenReturn(
+ mProviderIdentities);
+ establishServerConnection();
+ TrustManager[] trustManagers = mTrustManagerCaptor.getValue();
+ X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+ trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
+ Map<String, String> friendlyNames = new HashMap<>();
+ friendlyNames.put(
+ Locale.SIMPLIFIED_CHINESE.getISO3Language(), TEST_PROVIDER_CHINESE_NAME);
+
+ assertFalse(mOsuServerConnection.validateProvider(friendlyNames));
+ } finally {
+ session.finishMocking();
+ }
+ }
+
+ /**
+ * Verifies same language from different regions.
+ */
+ @Test
+ public void verifyValidateProviderWithSameLangButDifferentRegion() throws Exception {
+ // static mocking
+ MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(
+ ServiceProviderVerifier.class).startMocking();
+ try {
+ when(ServiceProviderVerifier.getProviderNames(any(X509Certificate.class))).thenReturn(
+ mProviderIdentities);
+ establishServerConnection();
+ TrustManager[] trustManagers = mTrustManagerCaptor.getValue();
+ X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+ trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
+ Map<String, String> friendlyNames = new HashMap<>();
+ friendlyNames.put(
+ Locale.CANADA.getISO3Language(), PROVIDER_NAME_VALID);
+
+ assertTrue(mOsuServerConnection.validateProvider(friendlyNames));
} finally {
session.finishMocking();
}
@@ -185,7 +264,7 @@
mOsuServerConnection.setEventCallback(mOsuServerCallbacks);
assertTrue(mOsuServerConnection.canValidateServer());
- assertTrue(mOsuServerConnection.connect(mValidServerUrl, mNetwork));
+ assertTrue(mOsuServerConnection.connect(mServerUrl, mNetwork));
mLooper.dispatchAll();
@@ -203,7 +282,7 @@
mOsuServerConnection.setEventCallback(mOsuServerCallbacks);
assertTrue(mOsuServerConnection.canValidateServer());
- assertTrue(mOsuServerConnection.connect(mValidServerUrl, mNetwork));
+ assertTrue(mOsuServerConnection.connect(mServerUrl, mNetwork));
mLooper.dispatchAll();
@@ -216,13 +295,16 @@
@Test
public void verifyInitAndConnectCertValidationFailure() throws Exception {
establishServerConnection();
+ List<X509Certificate> certificateList = PasspointProvisioningTestUtil.getOsuCertsForTest();
+ X509Certificate[] certificates = new X509Certificate[1];
+ certificates[0] = certificateList.get(0);
TrustManager[] trustManagers = mTrustManagerCaptor.getValue();
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
doThrow(new CertificateException()).when(mDelegate)
.getTrustedChainForServer(any(X509Certificate[].class), anyString(),
(Socket) isNull());
- trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
+ trustManager.checkServerTrusted(certificates, AUTH_TYPE);
verify(mOsuServerCallbacks).onServerValidationStatus(anyInt(), eq(false));
}
@@ -247,7 +329,9 @@
trustManager.checkServerTrusted(new X509Certificate[1], AUTH_TYPE);
verify(mOsuServerCallbacks).onServerValidationStatus(anyInt(), eq(true));
- assertFalse(mOsuServerConnection.validateProvider(Locale.US, PROVIDER_NAME_INVALID));
+ Map<String, String> providerNames = new HashMap<>();
+ providerNames.put(Locale.US.getISO3Language(), PROVIDER_NAME_INVALID);
+ assertFalse(mOsuServerConnection.validateProvider(providerNames));
} finally {
session.finishMocking();
}
@@ -475,13 +559,26 @@
}
}
+ /**
+ * Verifies initialization and opening URL connection failure for an HTTP URL (not HTTPS)
+ */
+ @Test
+ public void verifyInitAndNetworkOpenURLConnectionFailedWithHttpUrl() throws Exception {
+ mServerUrl = new URL(TEST_INVALID_URL);
+ mOsuServerConnection.init(mTlsContext, mDelegate);
+ mOsuServerConnection.setEventCallback(mOsuServerCallbacks);
+
+ assertTrue(mOsuServerConnection.canValidateServer());
+ assertFalse(mOsuServerConnection.connect(mServerUrl, mNetwork));
+ }
+
private void establishServerConnection() throws Exception {
mOsuServerConnection.init(mTlsContext, mDelegate);
mOsuServerConnection.setEventCallback(mOsuServerCallbacks);
verify(mTlsContext).init(isNull(), mTrustManagerCaptor.capture(), isNull());
assertTrue(mOsuServerConnection.canValidateServer());
- assertTrue(mOsuServerConnection.connect(mValidServerUrl, mNetwork));
+ assertTrue(mOsuServerConnection.connect(mServerUrl, mNetwork));
mLooper.dispatchAll();
verify(mOsuServerCallbacks).onServerConnectionStatus(anyInt(), eq(true));
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java
index c76e2c8..7a81500 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java
@@ -25,6 +25,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +63,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mConfigStoreData.serializeData(out);
+ mConfigStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -77,7 +78,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mConfigStoreData.deserializeData(in, in.getDepth());
+ mConfigStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java
index 82cdb5a..5278e19 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java
@@ -32,6 +32,7 @@
import com.android.server.wifi.SIMAccessor;
import com.android.server.wifi.WifiConfigStore;
import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -213,7 +214,7 @@
final XmlSerializer out = new FastXmlSerializer();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- mConfigStoreData.serializeData(out);
+ mConfigStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class));
out.flush();
return outputStream.toByteArray();
}
@@ -228,7 +229,9 @@
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
- mConfigStoreData.deserializeData(in, in.getDepth());
+ mConfigStoreData.deserializeData(in, in.getDepth(),
+ WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION,
+ mock(WifiConfigStoreEncryptionUtil.class));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index 013c11f..c7d6604 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -232,7 +232,8 @@
* @param expectedConfig The expected installed Passpoint configuration
*/
private void verifyInstalledConfig(PasspointConfiguration expectedConfig) {
- List<PasspointConfiguration> installedConfigs = mManager.getProviderConfigs();
+ List<PasspointConfiguration> installedConfigs =
+ mManager.getProviderConfigs(TEST_CREATOR_UID, true);
assertEquals(1, installedConfigs.size());
assertEquals(expectedConfig, installedConfigs.get(0));
}
@@ -247,6 +248,7 @@
PasspointProvider provider = mock(PasspointProvider.class);
when(provider.installCertsAndKeys()).thenReturn(true);
lenient().when(provider.getConfig()).thenReturn(config);
+ lenient().when(provider.getCreatorUid()).thenReturn(TEST_CREATOR_UID);
return provider;
}
@@ -537,14 +539,14 @@
// Provider index start with 0, should be 1 after adding a provider.
assertEquals(1, mSharedDataSource.getProviderIndex());
- // Remove the provider.
- assertTrue(mManager.removeProvider(TEST_FQDN));
+ // Remove the provider as the creator app.
+ assertTrue(mManager.removeProvider(TEST_CREATOR_UID, false, TEST_FQDN));
verify(provider).uninstallCertsAndKeys();
verify(mWifiConfigManager).saveToStore(true);
verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess();
verify(mAppOpsManager).stopWatchingMode(any(AppOpsManager.OnOpChangedListener.class));
- assertTrue(mManager.getProviderConfigs().isEmpty());
+ assertTrue(mManager.getProviderConfigs(TEST_CREATOR_UID, false).isEmpty());
// Verify content in the data source.
assertTrue(mUserDataSource.getProviders().isEmpty());
@@ -580,13 +582,13 @@
// Provider index start with 0, should be 1 after adding a provider.
assertEquals(1, mSharedDataSource.getProviderIndex());
- // Remove the provider.
- assertTrue(mManager.removeProvider(TEST_FQDN));
+ // Remove the provider as a privileged non-creator app.
+ assertTrue(mManager.removeProvider(TEST_UID, true, TEST_FQDN));
verify(provider).uninstallCertsAndKeys();
verify(mWifiConfigManager).saveToStore(true);
verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess();
- assertTrue(mManager.getProviderConfigs().isEmpty());
+ assertTrue(mManager.getProviderConfigs(TEST_UID, true).isEmpty());
// Verify content in the data source.
assertTrue(mUserDataSource.getProviders().isEmpty());
@@ -710,7 +712,7 @@
*/
@Test
public void removeNonExistingProvider() throws Exception {
- assertFalse(mManager.removeProvider(TEST_FQDN));
+ assertFalse(mManager.removeProvider(TEST_CREATOR_UID, true, TEST_FQDN));
verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
verify(mWifiMetrics, never()).incrementNumPasspointProviderUninstallSuccess();
}
@@ -1171,8 +1173,8 @@
mUserDataSource.setProviders(providers);
// Verify the providers maintained by PasspointManager.
- assertEquals(1, mManager.getProviderConfigs().size());
- assertEquals(config, mManager.getProviderConfigs().get(0));
+ assertEquals(1, mManager.getProviderConfigs(TEST_CREATOR_UID, true).size());
+ assertEquals(config, mManager.getProviderConfigs(TEST_CREATOR_UID, true).get(0));
}
/**
@@ -1686,7 +1688,7 @@
verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE), eq(TEST_PACKAGE),
mAppOpChangedListenerCaptor.capture());
- assertEquals(1, mManager.getProviderConfigs().size());
+ assertEquals(1, mManager.getProviderConfigs(TEST_CREATOR_UID, true).size());
AppOpsManager.OnOpChangedListener listener = mAppOpChangedListenerCaptor.getValue();
assertNotNull(listener);
@@ -1700,6 +1702,45 @@
verify(mAppOpsManager).stopWatchingMode(mAppOpChangedListenerCaptor.getValue());
verify(mClientModeImpl).disconnectCommand();
- assertTrue(mManager.getProviderConfigs().isEmpty());
+ assertTrue(mManager.getProviderConfigs(TEST_CREATOR_UID, true).isEmpty());
+ }
+
+ /**
+ * Verify that removing a provider with a different UID will not succeed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void removeGetProviderWithDifferentUid() throws Exception {
+ PasspointConfiguration config = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI,
+ TEST_REALM);
+ PasspointProvider provider = createMockProvider(config);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn(
+ provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE));
+ verifyInstalledConfig(config);
+ verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
+ reset(mWifiMetrics);
+ reset(mWifiConfigManager);
+
+ // no profiles available for TEST_UID
+ assertTrue(mManager.getProviderConfigs(TEST_UID, false).isEmpty());
+ // 1 profile available for TEST_CREATOR_UID
+ assertFalse(mManager.getProviderConfigs(TEST_CREATOR_UID, false).isEmpty());
+
+ // Remove the provider as a non-privileged non-creator app.
+ assertFalse(mManager.removeProvider(TEST_UID, false, TEST_FQDN));
+ verify(provider, never()).uninstallCertsAndKeys();
+ verify(mWifiConfigManager, never()).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderUninstallSuccess();
+
+ // no profiles available for TEST_UID
+ assertTrue(mManager.getProviderConfigs(TEST_UID, false).isEmpty());
+ // 1 profile available for TEST_CREATOR_UID
+ assertFalse(mManager.getProviderConfigs(TEST_CREATOR_UID, false).isEmpty());
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
index 31229c5..c35d673 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
@@ -478,9 +478,10 @@
}
/**
- * Verify that there is no match when the provider's FQDN matches a domain name in the
- * Domain Name ANQP element but the provider's credential doesn't match the authentication
- * method provided in the NAI realm.
+ * Verify that Home provider is matched even when the provider's FQDN matches a domain name in
+ * the Domain Name ANQP element but the provider's credential doesn't match the authentication
+ * method provided in the NAI realm. This can happen when the infrastructure provider is not
+ * the identity provider, and authentication method matching is not required in the spec.
*
* @throws Exception
*/
@@ -509,7 +510,8 @@
anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null));
- assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap, mRoamingConsortium));
+ assertEquals(PasspointMatch.HomeProvider,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
}
/**
@@ -657,8 +659,8 @@
}
/**
- * Verify that there is no match when a roaming consortium OI matches an OI
- * in the roaming consortium ANQP element and but NAI realm is not matched.
+ * Verify that there is Roaming provider match when a roaming consortium OI matches an OI
+ * in the roaming consortium ANQP element and regardless of NAI realm mismatch.
*
* @throws Exception
*/
@@ -689,7 +691,7 @@
anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null));
- assertEquals(PasspointMatch.None,
+ assertEquals(PasspointMatch.RoamingProvider,
mProvider.match(anqpElementMap, mRoamingConsortium));
}
@@ -766,8 +768,14 @@
}
/**
- * Verify that there is no match when a roaming consortium OI matches an OI
+ * Verify that there is Roaming provider match when a roaming consortium OI matches an OI
* in the roaming consortium information element, but NAI realm is not matched.
+ * This can happen in roaming federation where the infrastructure provider is not the
+ * identity provider.
+ * Page 133 in the Hotspot2.0 specification states:
+ * Per subclause 11.25.8 of [2], if the value of HomeOI matches an OI in the Roaming
+ * Consortium advertised by a hotspot operator, successful authentication with that hotspot
+ * is possible.
*
* @throws Exception
*/
@@ -799,7 +807,7 @@
anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null));
- assertEquals(PasspointMatch.None,
+ assertEquals(PasspointMatch.RoamingProvider,
mProvider.match(anqpElementMap, mRoamingConsortium));
}
@@ -1354,4 +1362,167 @@
mProvider.setHasEverConnected(true);
assertTrue(mProvider.getHasEverConnected());
}
+
+ /**
+ * Verify that an expected WifiConfiguration will be returned for a Passpoint provider
+ * with a user credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchOtherPartnersDomainName() throws Exception {
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1.com");
+ homeSp.setOtherHomePartners(new String [] {"test3.com"});
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+
+ // Setup Domain Name ANQP element to test2.com and test3.com
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"test2.com", "test3.com"}));
+
+ assertEquals(PasspointMatch.HomeProvider,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
+ }
+
+ /**
+ * Verify that matching Any HomeOI results in a Home Provider match
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAnyHomeOi() throws Exception {
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1.com");
+ homeSp.setMatchAnyOis(new long[] {0x1234L, 0x2345L});
+ homeSp.setRoamingConsortiumOis(null);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+ Long[] anqpOis = new Long[] {0x1234L, 0xdeadL, 0xf0cdL};
+
+ // Setup Domain Name ANQP element to test2.com and test3.com
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"test2.com", "test3.com"}));
+ // Setup RCOIs advertised by the AP
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpOis));
+
+ assertEquals(PasspointMatch.HomeProvider,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
+ }
+
+ /**
+ * Verify that non-matching Any HomeOI results in a None Provider match
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAnyHomeOiNegative() throws Exception {
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1.com");
+ homeSp.setMatchAnyOis(new long[] {0x1234L, 0x2345L});
+ homeSp.setRoamingConsortiumOis(null);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+ Long[] anqpOis = new Long[] {0x12a4L, 0xceadL, 0xf0cdL};
+
+ // Setup Domain Name ANQP element to test2.com and test3.com
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"test2.com", "test3.com"}));
+ // Setup RCOIs advertised by the AP
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpOis));
+
+ assertEquals(PasspointMatch.None,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
+ }
+
+ /**
+ * Verify that matching All HomeOI results in a Home Provider match
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAllHomeOi() throws Exception {
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1.com");
+ homeSp.setMatchAllOis(new long[] {0x1234L, 0x2345L});
+ homeSp.setRoamingConsortiumOis(null);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+ Long[] anqpOis = new Long[] {0x1234L, 0x2345L, 0xabcdL, 0xdeadL, 0xf0cdL};
+
+ // Setup Domain Name ANQP element to test2.com and test3.com
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"test2.com", "test3.com"}));
+ // Setup RCOIs advertised by the AP
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpOis));
+
+ assertEquals(PasspointMatch.HomeProvider,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
+ }
+
+ /**
+ * Verify that non-matching All HomeOI results in a None Provider match
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchAllHomeOiNegative() throws Exception {
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1.com");
+ homeSp.setMatchAllOis(new long[] {0x1234L, 0x2345L});
+ homeSp.setRoamingConsortiumOis(null);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+
+ // 0x1234 matches, but 0x2345 does not
+ Long[] anqpOis = new Long[] {0x1234L, 0x5678L, 0xdeadL, 0xf0cdL};
+
+ // Setup Domain Name ANQP element to test2.com and test3.com
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"test2.com", "test3.com"}));
+ // Setup RCOIs advertised by the AP
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpOis));
+
+ assertEquals(PasspointMatch.None,
+ mProvider.match(anqpElementMap, mRoamingConsortium));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
index 10ce650..64a7f9e 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
@@ -97,7 +97,6 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import javax.net.ssl.SSLContext;
@@ -224,8 +223,8 @@
mPasspointManager, mWifiMetrics);
when(mOsuNetworkConnection.connect(any(WifiSsid.class), any(), any())).thenReturn(true);
when(mOsuServerConnection.connect(any(URL.class), any(Network.class))).thenReturn(true);
- when(mOsuServerConnection.validateProvider(any(Locale.class),
- any(String.class))).thenReturn(true);
+ when(mOsuServerConnection.validateProvider(
+ anyMap())).thenReturn(true);
when(mOsuServerConnection.canValidateServer()).thenReturn(true);
mPasspointProvisioner.enableVerboseLogging(1);
mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
@@ -728,8 +727,8 @@
*/
@Test
public void verifyProviderVerificationFailure() throws RemoteException {
- when(mOsuServerConnection.validateProvider(any(Locale.class),
- any(String.class))).thenReturn(false);
+ when(mOsuServerConnection.validateProvider(
+ anyMap())).thenReturn(false);
stopAfterStep(STEP_SERVER_CONNECT);
// Wait for OSU server validation callback
diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
index 15d9ef9..1091c1d 100644
--- a/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
@@ -3812,4 +3812,28 @@
mLooper.dispatchAll();
verify(mWifiNative).p2pStopFind();
}
+
+ /**
+ * Verify a network name which is too long is rejected.
+ */
+ @Test
+ public void testSendConnectMsgWithTooLongNetworkName() throws Exception {
+ mTestWifiP2pFastConnectionConfig.networkName = "DIRECT-xy-abcdefghijklmnopqrstuvw";
+ sendConnectMsg(mClientMessenger, mTestWifiP2pFastConnectionConfig);
+ verify(mClientHandler).sendMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(WifiP2pManager.CONNECT_FAILED, message.what);
+ }
+
+ /**
+ * Verify a network name which is too short is rejected.
+ */
+ @Test
+ public void testSendConnectMsgWithTooShortNetworkName() throws Exception {
+ mTestWifiP2pFastConnectionConfig.networkName = "DIRECT-x";
+ sendConnectMsg(mClientMessenger, mTestWifiP2pFastConnectionConfig);
+ verify(mClientHandler).sendMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(WifiP2pManager.CONNECT_FAILED, message.what);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
index d2f22da..bd0ad32 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
@@ -18,6 +18,7 @@
package com.android.server.wifi.rtt;
import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -42,6 +43,7 @@
import android.hardware.wifi.V1_0.WifiStatusCode;
import android.net.MacAddress;
import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.ResponderConfig;
import androidx.test.filters.SmallTest;
@@ -465,6 +467,28 @@
}
}
+ /**
+ * Validation ranging with invalid bw and preamble combination will be ignored.
+ */
+ @Test
+ public void testRangingWithInvalidParameterCombination() throws Exception {
+ int cmdId = 88;
+ RangingRequest request = new RangingRequest.Builder().build();
+ ResponderConfig invalidConfig = new ResponderConfig(
+ MacAddress.fromString("08:09:08:07:06:88"), ResponderConfig.RESPONDER_AP, true,
+ ResponderConfig.CHANNEL_WIDTH_80MHZ, 0, 0, 0, ResponderConfig.PREAMBLE_HT);
+ ResponderConfig config = new ResponderConfig(MacAddress.fromString("08:09:08:07:06:89"),
+ ResponderConfig.RESPONDER_AP, true,
+ ResponderConfig.CHANNEL_WIDTH_80MHZ, 0, 0, 0, ResponderConfig.PREAMBLE_VHT);
+
+ // Add a ResponderConfig with invalid parameter, should be ignored.
+ request.mRttPeers.add(invalidConfig);
+ request.mRttPeers.add(config);
+ mDut.rangeRequest(cmdId, request, true);
+ verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture());
+ assertEquals(request.mRttPeers.size() - 1, mRttConfigCaptor.getValue().size());
+ }
+
// Utilities
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java b/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
deleted file mode 100644
index b707698..0000000
--- a/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 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.util;
-
-import static org.junit.Assert.*;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.io.File;
-import java.security.DigestException;
-
-/**
- * Unit tests for {@link com.android.server.wifi.util.DataIntegrityChecker}.
- */
-public class DataIntegrityCheckerTest {
- private static byte[] sGoodData = {1, 2, 3, 4};
- private static byte[] sBadData = {5, 6, 7, 8};
-
- /**
- * Verify that updating the integrity token with known data and alias will
- * pass the integrity test. This test ensure the expected outcome for
- * unedited data succeeds.
- *
- * @throws Exception
- */
- @Test
- @Ignore
- public void testIntegrityWithKnownDataAndKnownAlias() throws Exception {
- File integrityFile = File.createTempFile("testIntegrityWithKnownDataAndKnownAlias",
- ".tmp");
- DataIntegrityChecker dataIntegrityChecker = new DataIntegrityChecker(
- integrityFile.getParent());
- dataIntegrityChecker.update(sGoodData);
- assertTrue(dataIntegrityChecker.isOk(sGoodData));
- }
-
- /**
- * Verify that checking the integrity of unknown data and a known alias
- * will fail the integrity test. This test ensure the expected failure for
- * altered data, in fact, fails.
- *
- *
- * @throws Exception
- */
- @Test
- @Ignore
- public void testIntegrityWithUnknownDataAndKnownAlias() throws Exception {
- File integrityFile = File.createTempFile("testIntegrityWithUnknownDataAndKnownAlias",
- ".tmp");
- DataIntegrityChecker dataIntegrityChecker = new DataIntegrityChecker(
- integrityFile.getParent());
- dataIntegrityChecker.update(sGoodData);
- assertFalse(dataIntegrityChecker.isOk(sBadData));
- }
-
- /**
- * Verify a corner case where integrity of data that has never been
- * updated passes and adds the token to the keystore.
- *
- * @throws Exception
- */
- @Test(expected = DigestException.class)
- @Ignore
- public void testIntegrityWithoutUpdate() throws Exception {
- File tmpFile = File.createTempFile("testIntegrityWithoutUpdate", ".tmp");
-
- DataIntegrityChecker dataIntegrityChecker = new DataIntegrityChecker(
- tmpFile.getAbsolutePath());
-
- // the integrity data is not known, so isOk throws a DigestException
- assertTrue(dataIntegrityChecker.isOk(sGoodData));
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
index 45adffd..266a2ce 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
@@ -116,6 +116,82 @@
assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
}
+ /**
+ * Test that a PSK-SHA256+SAE network is detected as transition mode
+ */
+ @Test
+ public void testPskSha256SaeTransitionModeCheck() {
+ final String ssid = "WPA3-Transition";
+ String caps = "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0,
+ 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ assertTrue(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input));
+ }
+
+ /**
+ * Test that a PSK+SAE network is detected as transition mode
+ */
+ @Test
+ public void testPskSaeTransitionModeCheck() {
+ final String ssid = "WPA3-Transition";
+ String caps = "[WPA2-FT/PSK+PSK+SAE+FT/SAE-CCMP][ESS][WPS]";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0,
+ 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ assertTrue(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input));
+ }
+
+ /**
+ * Test that a PSK network is not detected as transition mode
+ */
+ @Test
+ public void testPskNotInTransitionModeCheck() {
+ final String ssid = "WPA2-Network";
+ String caps = "[WPA2-FT/PSK+PSK][ESS][WPS]";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0,
+ 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ assertFalse(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input));
+ }
+
+ /**
+ * Test that an SAE network is not detected as transition mode
+ */
+ @Test
+ public void testSaeNotInTransitionModeCheck() {
+ final String ssid = "WPA3-Network";
+ String caps = "[WPA2-FT/SAE+SAE][ESS][WPS]";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0,
+ 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ assertFalse(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input));
+ }
+
private static InformationElement createIE(int id, byte[] bytes) {
InformationElement ie = new InformationElement();
ie.id = id;
diff --git a/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java
index 8baacf6..0c9ed26 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java
@@ -1148,7 +1148,7 @@
}
private void setupMocks() throws Exception {
- when(mMockPkgMgr.getApplicationInfo(TEST_PACKAGE_NAME, 0))
+ when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PACKAGE_NAME), eq(0), anyInt()))
.thenReturn(mMockApplInfo);
when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
when(mMockAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, mUid, TEST_PACKAGE_NAME))
diff --git a/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
index 85b4a93..8f96bc1 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
@@ -35,7 +35,9 @@
import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -73,6 +75,13 @@
private static final int TEST_PHASE2_METHOD = WifiEnterpriseConfig.Phase2.MSCHAPV2;
private final String mXmlDocHeader = "XmlUtilTest";
+ private WifiConfigStoreEncryptionUtil mWifiConfigStoreEncryptionUtil = null;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
/**
* Verify that a open WifiConfiguration is serialized & deserialized correctly.
*/
@@ -101,6 +110,22 @@
}
/**
+ * Verify that a psk WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testPskWifiConfigurationSerializeDeserializeWithEncryption()
+ throws IOException, XmlPullParserException {
+ mWifiConfigStoreEncryptionUtil = mock(WifiConfigStoreEncryptionUtil.class);
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ EncryptedData encryptedData = new EncryptedData(new byte[0], new byte[0]);
+ when(mWifiConfigStoreEncryptionUtil.encrypt(pskNetwork.preSharedKey.getBytes()))
+ .thenReturn(encryptedData);
+ when(mWifiConfigStoreEncryptionUtil.decrypt(encryptedData))
+ .thenReturn(pskNetwork.preSharedKey.getBytes());
+ serializeDeserializeWifiConfiguration(pskNetwork);
+ }
+
+ /**
* Verify that a psk hidden WifiConfiguration is serialized & deserialized correctly.
*/
@Test
@@ -382,6 +407,37 @@
}
/**
+ * Verify that a WifiEnterpriseConfig object is serialized & deserialized correctly.
+ */
+ @Test
+ public void testWifiEnterpriseConfigSerializeDeserializeWithEncryption()
+ throws IOException, XmlPullParserException {
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, TEST_IDENTITY);
+ config.setFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, TEST_ANON_IDENTITY);
+ config.setFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, TEST_PASSWORD);
+ config.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, TEST_CLIENT_CERT);
+ config.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, TEST_CA_CERT);
+ config.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, TEST_SUBJECT_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, TEST_ENGINE);
+ config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, TEST_ENGINE_ID);
+ config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, TEST_PRIVATE_KEY_ID);
+ config.setFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, TEST_ALTSUBJECT_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, TEST_DOM_SUFFIX_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, TEST_CA_PATH);
+ config.setEapMethod(TEST_EAP_METHOD);
+ config.setPhase2Method(TEST_PHASE2_METHOD);
+
+ mWifiConfigStoreEncryptionUtil = mock(WifiConfigStoreEncryptionUtil.class);
+ EncryptedData encryptedData = new EncryptedData(new byte[0], new byte[0]);
+ when(mWifiConfigStoreEncryptionUtil.encrypt(TEST_PASSWORD.getBytes()))
+ .thenReturn(encryptedData);
+ when(mWifiConfigStoreEncryptionUtil.decrypt(encryptedData))
+ .thenReturn(TEST_PASSWORD.getBytes());
+ serializeDeserializeWifiEnterpriseConfig(config);
+ }
+
+ /**
* Verify that an illegal argument exception is thrown when trying to parse out a corrupted
* WifiEnterpriseConfig.
*
@@ -473,7 +529,8 @@
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
XmlUtil.writeDocumentStart(out, mXmlDocHeader);
- WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, configuration);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(
+ out, configuration, mWifiConfigStoreEncryptionUtil);
XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
return outputStream.toByteArray();
}
@@ -485,7 +542,10 @@
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
- return WifiConfigurationXmlUtil.parseFromXml(in, in.getDepth());
+ return WifiConfigurationXmlUtil.parseFromXml(
+ in, in.getDepth(),
+ mWifiConfigStoreEncryptionUtil != null,
+ mWifiConfigStoreEncryptionUtil);
}
/**
@@ -593,7 +653,8 @@
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
XmlUtil.writeDocumentStart(out, mXmlDocHeader);
- WifiEnterpriseConfigXmlUtil.writeToXml(out, config);
+ WifiEnterpriseConfigXmlUtil.writeToXml(
+ out, config, mWifiConfigStoreEncryptionUtil);
XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
return outputStream.toByteArray();
}
@@ -604,7 +665,9 @@
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
- return WifiEnterpriseConfigXmlUtil.parseFromXml(in, in.getDepth());
+ return WifiEnterpriseConfigXmlUtil.parseFromXml(
+ in, in.getDepth(), mWifiConfigStoreEncryptionUtil != null,
+ mWifiConfigStoreEncryptionUtil);
}
private void serializeDeserializeWifiEnterpriseConfig(WifiEnterpriseConfig config)