summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Stefani <luca.stefani.ge1@gmail.com>2020-03-07 13:20:46 +0100
committerLuca Stefani <luca.stefani.ge1@gmail.com>2020-03-07 13:20:46 +0100
commit876987a7579be182a3e4f86947bec75434be9bc4 (patch)
tree9a2d569d8fdbb69ca255c0c87c04eafca5e4bf33
parentfb7bd6cb89ad593e571f864866c88d577bff2ce5 (diff)
parentaa0a79a0e9aa1a6c0c6a1c83360f6f1048fc59db (diff)
downloadandroid_frameworks_opt_net_wifi-876987a7579be182a3e4f86947bec75434be9bc4.tar.gz
android_frameworks_opt_net_wifi-876987a7579be182a3e4f86947bec75434be9bc4.tar.bz2
android_frameworks_opt_net_wifi-876987a7579be182a3e4f86947bec75434be9bc4.zip
Merge tag 'android-10.0.0_r31' into lineage-17.1-android-10.0.0_r31
Android 10.0.0 release 31 * tag 'android-10.0.0_r31': (36 commits) fix soft reboot caused by KeyStore exception p2p: validate the network name of a group Notification to set MAC randomization setting MAC randomization SSID hotlist support Fix race in StaEvents metrics collection Wifi: Fix connectivity issues with PSK-SHA256+SAE mode APs Reset num saved networks with mac randomization before counting Fix boot regression from KeyStore being slow [MAC rand] Fix unit test slowness WifiConfigStoreEncryptionUtil: Use 256 bit secret key Update the WifiConfig each time evalutor return a candidate. Revert submission [WPA3] Fix WPA3-Personal transition mode WifiConfigStore: Encrypt credentials for networks (4/4) WifiConfigStore: Encrypt credentials for networks (3/4) WifiConfigStore: Encrypt credentials for networks (2/4) WifiConfigStore: Encrypt credentials for networks (1/4) [EAP-SIM] Add NAI realm decoration to pseudonym [WifiRtt] add check to verify bw and preamble combination valid [MAC rand] Removing persistent storage ... Change-Id: I66be9db60b65cdd4ed515a48bb8b0c69b66f860a
-rw-r--r--service/java/com/android/server/wifi/ClientModeImpl.java68
-rw-r--r--service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java123
-rw-r--r--service/java/com/android/server/wifi/ConnectionFailureNotifier.java162
-rw-r--r--service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java10
-rw-r--r--service/java/com/android/server/wifi/DeviceConfigFacade.java21
-rw-r--r--service/java/com/android/server/wifi/FrameworkFacade.java33
-rw-r--r--service/java/com/android/server/wifi/HalDeviceManager.java14
-rw-r--r--service/java/com/android/server/wifi/MacAddressUtil.java139
-rw-r--r--service/java/com/android/server/wifi/NetworkListStoreData.java57
-rw-r--r--service/java/com/android/server/wifi/NetworkRequestStoreData.java9
-rw-r--r--service/java/com/android/server/wifi/NetworkSuggestionStoreData.java58
-rw-r--r--service/java/com/android/server/wifi/RandomizedMacStoreData.java10
-rw-r--r--service/java/com/android/server/wifi/SsidSetStoreData.java9
-rw-r--r--service/java/com/android/server/wifi/SupplicantStaIfaceHal.java4
-rw-r--r--service/java/com/android/server/wifi/SupplicantStaNetworkHal.java16
-rw-r--r--service/java/com/android/server/wifi/WakeupConfigStoreData.java9
-rw-r--r--service/java/com/android/server/wifi/WifiConfigManager.java59
-rw-r--r--service/java/com/android/server/wifi/WifiConfigStore.java245
-rw-r--r--service/java/com/android/server/wifi/WifiCountryCode.java12
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java37
-rw-r--r--service/java/com/android/server/wifi/WifiLockManager.java12
-rw-r--r--service/java/com/android/server/wifi/WifiMetrics.java10
-rw-r--r--service/java/com/android/server/wifi/WifiNetworkFactory.java159
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java35
-rw-r--r--service/java/com/android/server/wifi/WifiVendorHal.java8
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java10
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java9
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointManager.java30
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java12
-rw-r--r--service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java25
-rw-r--r--service/java/com/android/server/wifi/rtt/RttNative.java15
-rw-r--r--service/java/com/android/server/wifi/util/EncryptedData.java27
-rw-r--r--service/java/com/android/server/wifi/util/ScanResultUtil.java4
-rw-r--r--service/java/com/android/server/wifi/util/TelephonyUtil.java40
-rw-r--r--service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java (renamed from service/java/com/android/server/wifi/util/DataIntegrityChecker.java)141
-rw-r--r--service/java/com/android/server/wifi/util/XmlUtil.java630
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java155
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ConnectionFailureNotifierTest.java184
-rw-r--r--tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java8
-rw-r--r--tests/wifitests/src/com/android/server/wifi/MacAddressUtilTest.java87
-rw-r--r--tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java8
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java12
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java6
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java73
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java302
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java11
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java5
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java219
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java111
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java8
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java7
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java65
-rw-r--r--tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java24
-rw-r--r--tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java24
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java69
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java76
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java71
62 files changed, 2649 insertions, 1173 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index b36e0cb7b..0468f5ac3 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -163,7 +163,7 @@ public class ClientModeImpl extends StateMachine {
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;
@@ -765,6 +765,7 @@ public class ClientModeImpl extends StateMachine {
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.
@@ -818,7 +819,8 @@ public class ClientModeImpl extends StateMachine {
mSupplicantStateTracker =
mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this);
-
+ mConnectionFailureNotifier = mWifiInjector.makeConnectionFailureNotifier(
+ mWifiConnectivityManager);
mLinkProperties = new LinkProperties();
mMcastLockManagerFilterController = new McastLockManagerFilterController();
@@ -1787,12 +1789,14 @@ public class ClientModeImpl extends StateMachine {
* 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();
@@ -1803,10 +1807,13 @@ public class ClientModeImpl extends StateMachine {
* 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();
@@ -3160,6 +3167,16 @@ public class ClientModeImpl extends StateMachine {
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) {
@@ -3387,12 +3404,14 @@ public class ClientModeImpl extends StateMachine {
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);
@@ -3400,7 +3419,7 @@ public class ClientModeImpl extends StateMachine {
mWifiNative.setMacAddress(mInterfaceName, newMac);
Log.d(TAG, "ConnectedMacRandomization SSID(" + config.getPrintableSsid()
+ "). setMacAddress(" + newMac.toString() + ") from "
- + currentMac.toString() + " = " + setMacSuccess);
+ + currentMacString + " = " + setMacSuccess);
}
}
@@ -3696,11 +3715,13 @@ public class ClientModeImpl extends StateMachine {
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. */
@@ -4475,14 +4496,25 @@ public class ClientModeImpl extends StateMachine {
config.enterpriseConfig.getEapMethod())) {
String anonymousIdentity =
mWifiNative.getEapAnonymousIdentity(mInterfaceName);
- if (mVerboseLoggingEnabled) {
- log("EAP Pseudonym: " + anonymousIdentity);
- }
- if (!TelephonyUtil.isAnonymousAtRealmIdentity(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);
- mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
+ } else {
+ // Clear any stored pseudonyms
+ config.enterpriseConfig.setAnonymousIdentity(null);
}
+ mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
}
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
@@ -4535,7 +4567,8 @@ public class ClientModeImpl extends StateMachine {
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");
@@ -5372,7 +5405,6 @@ public class ClientModeImpl extends StateMachine {
.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 000000000..f4f89f09e
--- /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 000000000..bbef2ff6e
--- /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 0c064884c..b71d5a023 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 @@ public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData
}
@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 @@ public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData
}
@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
index a9889f424..25cc2f72d 100644
--- a/service/java/com/android/server/wifi/DeviceConfigFacade.java
+++ b/service/java/com/android/server/wifi/DeviceConfigFacade.java
@@ -17,7 +17,9 @@
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;
@@ -105,4 +107,23 @@ public class DeviceConfigFacade {
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 f3c5d4b3d..fe3027e50 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.ServiceManager;
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 @@ import com.android.server.wifi.util.WifiAsyncChannel;
*/
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 @@ public class FrameworkFacade {
}
/**
+ * 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 class FrameworkFacade {
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 d2ad47f48..e10234f68 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -1723,9 +1723,11 @@ public class HalDeviceManager {
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) {
@@ -1780,8 +1782,10 @@ public class HalDeviceManager {
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/MacAddressUtil.java b/service/java/com/android/server/wifi/MacAddressUtil.java
new file mode 100644
index 000000000..4739b6141
--- /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 696647185..52e655b1e 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.os.Process;
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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
}
@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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
*
* @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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
&& 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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
*
* @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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
// 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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
*
* @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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
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 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData
+ 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 8d1244f05..7457079ae 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;
@@ -87,13 +89,16 @@ public class NetworkRequestStoreData implements WifiConfigStore.StoreData {
}
@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) {
diff --git a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java
index 9627a9daa..e973bdbe2 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 android.util.Pair;
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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
}
@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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
// 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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
* @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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
+ 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 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData {
+ 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 1e4d972ef..ecbd7177f 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 @@ public class RandomizedMacStoreData implements WifiConfigStore.StoreData {
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 @@ public class RandomizedMacStoreData implements WifiConfigStore.StoreData {
}
@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/SsidSetStoreData.java b/service/java/com/android/server/wifi/SsidSetStoreData.java
index 7d1b99340..36b547cd2 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 @@ public class SsidSetStoreData implements WifiConfigStore.StoreData {
}
@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 @@ public class SsidSetStoreData implements WifiConfigStore.StoreData {
}
@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 86518c761..8db7c90ee 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -3074,10 +3074,10 @@ public class SupplicantStaIfaceHal {
* 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 9255fc231..dd56b5f57 100644
--- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
@@ -339,19 +339,11 @@ public class SupplicantStaNetworkHal {
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 d191ee3d6..847d8fbbc 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 @@ public class WakeupConfigStoreData implements StoreData {
}
@Override
- public void serializeData(XmlSerializer out)
+ public void serializeData(XmlSerializer out,
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
writeFeatureState(out);
@@ -141,7 +144,9 @@ public class WakeupConfigStoreData implements StoreData {
}
@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 d1734d445..c617b9e1f 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -76,8 +76,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.crypto.Mac;
-
/**
* This class provides the APIs to manage configured Wi-Fi networks.
* It deals with the following:
@@ -276,8 +274,8 @@ public class WifiConfigManager {
private final WifiPermissionsUtil mWifiPermissionsUtil;
private final WifiPermissionsWrapper mWifiPermissionsWrapper;
private final WifiInjector mWifiInjector;
+ private final MacAddressUtil mMacAddressUtil;
private boolean mConnectedMacRandomzationSupported;
- private final Mac mMac;
/**
* Local log used for debugging any WifiConfigManager issues.
@@ -320,6 +318,7 @@ public class WifiConfigManager {
private final int mMaxNumActiveChannelsForPartialScans;
private final FrameworkFacade mFrameworkFacade;
+ private final DeviceConfigFacade mDeviceConfigFacade;
/**
* Verbose logging flag. Toggled by developer options.
@@ -377,6 +376,7 @@ public class WifiConfigManager {
private boolean mPnoFrequencyCullingEnabled = false;
private boolean mPnoRecencySortingEnabled = false;
+ private Set<String> mRandomizationFlakySsidHotlist;
@@ -394,7 +394,8 @@ public class WifiConfigManager {
NetworkListUserStoreData networkListUserStoreData,
DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData,
RandomizedMacStoreData randomizedMacStoreData,
- FrameworkFacade frameworkFacade, Looper looper) {
+ FrameworkFacade frameworkFacade, Looper looper,
+ DeviceConfigFacade deviceConfigFacade) {
mContext = context;
mClock = clock;
mUserManager = userManager;
@@ -446,17 +447,21 @@ public class WifiConfigManager {
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.");
}
- mMac = WifiConfigurationUtil.obtainMacRandHashFunction(Process.WIFI_UID);
- if (mMac == null) {
- Log.wtf(TAG, "Failed to obtain secret for MAC randomization."
- + " All randomized MAC addresses are lost!");
- }
+ mMacAddressUtil = mWifiInjector.getMacAddressUtil();
}
/**
@@ -508,7 +513,18 @@ public class WifiConfigManager {
mRandomizedMacAddressMapping.remove(config.getSsidAndSecurityTypeString());
}
}
- return WifiConfigurationUtil.calculatePersistentMacForConfiguration(config, mMac);
+ MacAddress result = mMacAddressUtil.calculatePersistentMacForConfiguration(
+ config, mMacAddressUtil.obtainMacRandHashFunction(Process.WIFI_UID));
+ if (result == null) {
+ 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. "
+ + "Using locally generated MAC address instead.");
+ result = MacAddress.createRandomUnicastAddress();
+ }
+ return result;
}
/**
@@ -1525,6 +1541,19 @@ public class WifiConfigManager {
}
/**
+ * 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) {
@@ -3131,7 +3160,8 @@ public class WifiConfigManager {
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;
@@ -3170,7 +3200,8 @@ public class WifiConfigManager {
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;
@@ -3180,8 +3211,8 @@ public class WifiConfigManager {
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(),
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index e189d00e1..42d9f82cb 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -35,8 +35,8 @@ import com.android.internal.annotations.VisibleForTesting;
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 +53,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -62,6 +61,7 @@ import java.util.HashSet;
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}.
@@ -101,22 +101,36 @@ public class WifiConfigStore {
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";
- private static final String XML_TAG_INTEGRITY_ENCRYPTED_DATA = "EncryptedData";
- private static final String XML_TAG_INTEGRITY_IV = "IV";
/**
* Current config store data version. This will be incremented for any additions.
*/
- private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 2;
+ 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.
*/
- private static final int INTEGRITY_CONFIG_STORE_DATA_VERSION = 2;
+ 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.
@@ -157,12 +171,6 @@ public class WifiConfigStore {
put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL);
put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS);
}};
-
- @VisibleForTesting
- public static final EncryptedData ZEROED_ENCRYPTED_DATA =
- new EncryptedData(
- new byte[EncryptedData.ENCRYPTED_DATA_LENGTH],
- new byte[EncryptedData.IV_LENGTH]);
/**
* Handler instance to post alarm timeouts to
*/
@@ -280,9 +288,11 @@ public class WifiConfigStore {
* @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()) {
@@ -291,17 +301,22 @@ public class WifiConfigStore {
}
}
File file = new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId));
- DataIntegrityChecker dataIntegrityChecker = new DataIntegrityChecker(file.getName());
- return new StoreFile(file, fileId, dataIntegrityChecker);
+ 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);
}
/**
@@ -309,14 +324,18 @@ public class WifiConfigStore {
* 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;
}
@@ -427,88 +446,18 @@ public class WifiConfigStore {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
out.setOutput(outputStream, StandardCharsets.UTF_8.name());
- // To compute integrity, write zeroes in the integrity fields. Once the integrity is
- // computed, go back and modfiy the XML fields in place with the computed values.
- writeDocumentMetadata(out, ZEROED_ENCRYPTED_DATA);
+ // 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);
-
- byte[] outBytes = outputStream.toByteArray();
- EncryptedData encryptedData = storeFile.computeIntegrity(outBytes);
- if (encryptedData == null) {
- // should never happen, this is a fatal failure. Abort file write.
- Log.wtf(TAG, "Failed to compute integrity, failing write");
- return null;
- }
- return rewriteDocumentMetadataRawBytes(outBytes, encryptedData);
- }
-
- /**
- * Helper method to write the metadata at the start of every config store file.
- * The metadata consists of:
- * a) Version
- * b) Integrity data computed on the data contents.
- */
- private void writeDocumentMetadata(XmlSerializer out, EncryptedData encryptedData)
- throws XmlPullParserException, IOException {
- // First XML header.
- XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
- // Next version.
- XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION);
-
- // Next integrity data.
- XmlUtil.writeNextSectionStart(out, XML_TAG_HEADER_INTEGRITY);
- XmlUtil.writeNextValue(out, XML_TAG_INTEGRITY_ENCRYPTED_DATA,
- encryptedData.getEncryptedData());
- XmlUtil.writeNextValue(out, XML_TAG_INTEGRITY_IV, encryptedData.getIv());
- XmlUtil.writeNextSectionEnd(out, XML_TAG_HEADER_INTEGRITY);
- }
-
- /**
- * Helper method to generate the raw bytes containing the the metadata at the start of every
- * config store file.
- *
- * NOTE: This does not create a fully formed XML document (the start tag is not closed for
- * example). This only creates the top portion of the XML which contains the modified
- * integrity data & version along with the XML prolog (metadata):
- * <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
- * <WifiConfigStoreData>
- * <int name="Version" value="2" />
- * <Integrity>
- * <byte-array name="EncryptedData" num="48">!EncryptedData!</byte-array>
- * <byte-array name="IV" num="12">!IV!</byte-array>
- * </Integrity>
- */
- private byte[] generateDocumentMetadataRawBytes(EncryptedData encryptedData)
- throws XmlPullParserException, IOException {
- final XmlSerializer outXml = new FastXmlSerializer();
- final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
- writeDocumentMetadata(outXml, encryptedData);
- outXml.endDocument();
- return outStream.toByteArray();
- }
-
- /**
- * Helper method to rewrite the existing document metadata in the incoming raw bytes in
- * |inBytes| with the new document metadata created with the provided |encryptedData|.
- *
- * NOTE: This assumes that the metadata in existing XML inside |inBytes| aligns exactly
- * with the new metadata created. So, a simple in place rewrite of the first few bytes (
- * corresponding to metadata section of the document) from |inBytes| will preserve the overall
- * document structure.
- */
- private byte[] rewriteDocumentMetadataRawBytes(byte[] inBytes, EncryptedData encryptedData)
- throws XmlPullParserException, IOException {
- byte[] replaceMetadataBytes = generateDocumentMetadataRawBytes(encryptedData);
- ByteBuffer outByteBuffer = ByteBuffer.wrap(inBytes);
- outByteBuffer.put(replaceMetadataBytes);
- return outByteBuffer.array();
+ return outputStream.toByteArray();
}
/**
@@ -629,10 +578,11 @@ public class WifiConfigStore {
}
// 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);
}
}
@@ -654,7 +604,8 @@ public class WifiConfigStore {
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();
@@ -665,21 +616,10 @@ public class WifiConfigStore {
int rootTagDepth = in.getDepth() + 1;
XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
- int version = parseVersionFromXml(in);
- // Version 2 onwards contains integrity data, so check the integrity of the file.
- if (version >= INTEGRITY_CONFIG_STORE_DATA_VERSION) {
- EncryptedData integrityData = parseIntegrityDataFromXml(in, rootTagDepth);
- // To compute integrity, write zeroes in the integrity fields.
- dataBytes = rewriteDocumentMetadataRawBytes(dataBytes, ZEROED_ENCRYPTED_DATA);
- if (!storeFile.checkIntegrity(dataBytes, integrityData)) {
- Log.wtf(TAG, "Integrity mismatch, discarding data from " + storeFile.mFileName);
- return;
- }
- } else {
- // 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.
- // Integrity will be computed for the next write.
- Log.d(TAG, "No integrity data to check; thus vacously true");
+ @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];
@@ -695,14 +635,15 @@ public class WifiConfigStore {
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());
}
/**
@@ -712,7 +653,7 @@ public class WifiConfigStore {
* @param in XmlPullParser instance pointing to the XML stream.
* @return version number retrieved from the Xml stream.
*/
- private static int parseVersionFromXml(XmlPullParser in)
+ private static @Version int parseVersionFromXml(XmlPullParser in)
throws XmlPullParserException, IOException {
int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION);
if (version < INITIAL_CONFIG_STORE_DATA_VERSION
@@ -723,22 +664,15 @@ public class WifiConfigStore {
}
/**
- * Parse the integrity data structure from the XML stream.
- * This is used for both the shared and user config store data.
+ * 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.
- * @return Instance of {@link EncryptedData} retrieved from the Xml stream.
*/
- private static @NonNull EncryptedData parseIntegrityDataFromXml(
- XmlPullParser in, int outerTagDepth)
+ private static void parseAndDiscardIntegrityDataFromXml(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
XmlUtil.gotoNextSectionWithName(in, XML_TAG_HEADER_INTEGRITY, outerTagDepth);
- byte[] encryptedData =
- (byte[]) XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_ENCRYPTED_DATA);
- byte[] iv =
- (byte[]) XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_IV);
- return new EncryptedData(encryptedData, iv);
+ XmlUtil.EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
}
/**
@@ -746,6 +680,13 @@ public class WifiConfigStore {
*/
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 =>");
@@ -790,14 +731,14 @@ public class WifiConfigStore {
/**
* Integrity checking for the store file.
*/
- private final DataIntegrityChecker mDataIntegrityChecker;
+ private final WifiConfigStoreEncryptionUtil mEncryptionUtil;
public StoreFile(File file, @StoreFileId int fileId,
- @NonNull DataIntegrityChecker dataIntegrityChecker) {
+ @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) {
mAtomicFile = new AtomicFile(file);
mFileName = file.getAbsolutePath();
mFileId = fileId;
- mDataIntegrityChecker = dataIntegrityChecker;
+ mEncryptionUtil = encryptionUtil;
}
/**
@@ -810,6 +751,13 @@ public class WifiConfigStore {
}
/**
+ * @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
@@ -862,33 +810,6 @@ public class WifiConfigStore {
// Reset the pending write data after write.
mWriteData = null;
}
-
- /**
- * Compute integrity of |dataWithZeroedIntegrityFields| to be written to the file.
- *
- * @param dataWithZeroedIntegrityFields raw data to be written to the file with the
- * integrity fields zeroed out for integrity
- * calculation.
- * @return Instance of {@link EncryptedData} holding the encrypted integrity data for the
- * raw data to be written to the file.
- */
- public EncryptedData computeIntegrity(byte[] dataWithZeroedIntegrityFields) {
- return mDataIntegrityChecker.compute(dataWithZeroedIntegrityFields);
- }
-
- /**
- * Check integrity of |dataWithZeroedIntegrityFields| read from the file with the integrity
- * data parsed from the file.
- * @param dataWithZeroedIntegrityFields raw data read from the file with the integrity
- * fields zeroed out for integrity calculation.
- * @param parsedEncryptedData Instance of {@link EncryptedData} parsed from the integrity
- * fields in the raw data.
- * @return true if the integrity matches, false otherwise.
- */
- public boolean checkIntegrity(byte[] dataWithZeroedIntegrityFields,
- EncryptedData parsedEncryptedData) {
- return mDataIntegrityChecker.isOk(dataWithZeroedIntegrityFields, parsedEncryptedData);
- }
}
/**
@@ -908,8 +829,10 @@ public class WifiConfigStore {
* 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;
/**
@@ -918,10 +841,14 @@ public class WifiConfigStore {
* @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/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index 499c159fa..f9d147c6b 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -84,18 +84,6 @@ public class WifiCountryCode {
}
/**
- * 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
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index fe9ebea17..339e169f6 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.annotation.NonNull;
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;
@@ -154,6 +155,8 @@ public class WifiInjector {
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) {
@@ -168,8 +171,11 @@ public class WifiInjector {
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);
@@ -241,7 +247,7 @@ public class WifiInjector {
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
@@ -251,7 +257,7 @@ public class WifiInjector {
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);
@@ -621,6 +627,17 @@ public class WifiInjector {
}
/**
+ * 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.
*/
@@ -695,6 +712,18 @@ public class WifiInjector {
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.
*/
@@ -753,4 +782,8 @@ public class WifiInjector {
public HostapdHal getHostapdHal() {
return mHostapdHal;
}
+
+ public String getWifiStackPackageName() {
+ return mContext.getPackageName();
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java
index e292a84d8..45e68824c 100644
--- a/service/java/com/android/server/wifi/WifiLockManager.java
+++ b/service/java/com/android/server/wifi/WifiLockManager.java
@@ -183,10 +183,6 @@ public class WifiLockManager {
* @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 @@ public class WifiLockManager {
}
}
- 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 1b7e8cdb3..7578d3723 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -2942,6 +2942,7 @@ public class WifiMetrics {
public void updateSavedNetworks(List<WifiConfiguration> networks) {
synchronized (mLock) {
mWifiLogProto.numSavedNetworks = networks.size();
+ mWifiLogProto.numSavedNetworksWithMacRandomization = 0;
mWifiLogProto.numOpenNetworks = 0;
mWifiLogProto.numLegacyPersonalNetworks = 0;
mWifiLogProto.numLegacyEnterpriseNetworks = 0;
@@ -3790,9 +3791,11 @@ public class WifiMetrics {
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) {
@@ -4624,6 +4627,7 @@ public class WifiMetrics {
}
}
mWifiUsabilityStatsCounter = 0;
+ mWifiUsabilityStatsEntriesList.clear();
}
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index c375e9f59..4b5866b44 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -82,6 +82,8 @@ public class WifiNetworkFactory extends NetworkFactory {
@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
@@ -230,37 +232,10 @@ public class WifiNetworkFactory extends NetworkFactory {
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();
}
}
@@ -453,7 +428,12 @@ public class WifiNetworkFactory extends NetworkFactory {
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);
}
/**
@@ -612,10 +592,19 @@ public class WifiNetworkFactory extends NetworkFactory {
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();
+ }
}
}
@@ -712,6 +701,11 @@ public class WifiNetworkFactory extends NetworkFactory {
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 =
@@ -724,6 +718,32 @@ public class WifiNetworkFactory extends NetworkFactory {
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) {
@@ -762,7 +782,6 @@ public class WifiNetworkFactory extends NetworkFactory {
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;
@@ -770,7 +789,8 @@ public class WifiNetworkFactory extends NetworkFactory {
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.
@@ -966,6 +986,7 @@ public class WifiNetworkFactory extends NetworkFactory {
mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier;
mActiveSpecificNetworkRequest = null;
mActiveSpecificNetworkRequestSpecifier = null;
+ mActiveMatchedScanResults = null;
mPendingConnectionSuccess = false;
// Cancel connection timeout alarm.
cancelConnectionTimeout();
@@ -974,7 +995,7 @@ public class WifiNetworkFactory extends NetworkFactory {
// 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.
@@ -1112,7 +1133,8 @@ public class WifiNetworkFactory extends NetworkFactory {
}
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...");
@@ -1300,6 +1322,65 @@ public class WifiNetworkFactory extends NetworkFactory {
}
/**
+ * 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) {
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index ed796c0a6..54c2b6121 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -561,10 +561,6 @@ public class WifiServiceImpl extends BaseWifiService {
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));
@@ -2346,18 +2342,16 @@ public class WifiServiceImpl extends BaseWifiService {
@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, uid)) {
- 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);
}
/**
@@ -2370,13 +2364,10 @@ public class WifiServiceImpl extends BaseWifiService {
@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, uid)) {
- 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();
@@ -2385,7 +2376,7 @@ public class WifiServiceImpl extends BaseWifiService {
PackageManager.FEATURE_WIFI_PASSPOINT)) {
return new ArrayList<>();
}
- return mClientModeImpl.syncGetPasspointConfigs(mClientModeImplChannel);
+ return mClientModeImpl.syncGetPasspointConfigs(mClientModeImplChannel, privileged);
}
/**
@@ -2869,6 +2860,10 @@ public class WifiServiceImpl extends BaseWifiService {
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(
() -> {
@@ -3012,7 +3007,7 @@ public class WifiServiceImpl extends BaseWifiService {
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);
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 464a34007..e780c10c4 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -1072,7 +1072,7 @@ public class WifiVendorHal {
/**
* 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 @@ public class WifiVendorHal {
/**
* 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 @@ public class WifiVendorHal {
* @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];
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java
index 419ea7993..7f5a6b408 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 @@ public class PasspointConfigSharedStoreData implements WifiConfigStore.StoreData
}
@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 0114cfb21..123cf8982 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.internal.util.XmlUtils;
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 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData {
}
@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 e54cee8e9..c46873761 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -288,7 +288,7 @@ public class PasspointManager {
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);
}
}
@@ -658,18 +658,26 @@ public class PasspointManager {
/**
* 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 */);
@@ -702,12 +710,17 @@ public class PasspointManager {
*
* 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;
}
@@ -1007,7 +1020,8 @@ public class PasspointManager {
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();
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
index ec8a009d9..148af39c6 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 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva
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/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index a6098be2b..532659102 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 com.android.server.wifi.util.WifiPermissionsWrapper;
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 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub {
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;
@@ -3260,6 +3266,23 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub {
}
/**
+ * 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.
@@ -3269,7 +3292,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub {
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 eaf947062..ffbf5bef9 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 @@ public class RttNative extends IWifiRttControllerEventCallback.Stub {
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 @@ public class RttNative extends IWifiRttControllerEventCallback.Stub {
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/util/EncryptedData.java b/service/java/com/android/server/wifi/util/EncryptedData.java
index 91342d335..baec20426 100644
--- a/service/java/com/android/server/wifi/util/EncryptedData.java
+++ b/service/java/com/android/server/wifi/util/EncryptedData.java
@@ -18,21 +18,19 @@ package com.android.server.wifi.util;
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 {
- public static final int ENCRYPTED_DATA_LENGTH = 48;
- public static final int IV_LENGTH = 12;
-
private final byte[] mEncryptedData;
private final byte[] mIv;
public EncryptedData(byte[] encryptedData, byte[] iv) {
- Preconditions.checkNotNull(encryptedData, iv);
- Preconditions.checkState(encryptedData.length == ENCRYPTED_DATA_LENGTH,
- "encryptedData.length=" + encryptedData.length);
- Preconditions.checkState(iv.length == IV_LENGTH, "iv.length=" + iv.length);
+ Preconditions.checkNotNull(encryptedData);
+ Preconditions.checkNotNull(iv);
mEncryptedData = encryptedData;
mIv = iv;
}
@@ -44,4 +42,17 @@ public class EncryptedData {
public byte[] getIv() {
return mIv;
}
+
+ @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 39e9d2c40..b92483899 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 @@ public class ScanResultUtil {
/**
* 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 4af40ddf2..3154df978 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.net.wifi.WifiEnterpriseConfig;
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 class TelephonyUtil {
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/DataIntegrityChecker.java b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java
index 6f03a4861..46bf0fee1 100644
--- a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
+++ b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java
@@ -17,22 +17,22 @@
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.io.IOException;
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;
@@ -43,34 +43,27 @@ 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>.
+ * Tools to help encrypt/decrypt
*/
-public class DataIntegrityChecker {
- private static final String TAG = "DataIntegrityChecker";
+public class WifiConfigStoreEncryptionUtil {
+ private static final String TAG = "WifiConfigStoreEncryptionUtil";
- private static final String ALIAS_SUFFIX = ".data-integrity-checker-key";
+ private static final String ALIAS_SUFFIX = ".data-encryption-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 int KEY_LENGTH = 256;
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[], EncryptedData)} always return true.
- */
- private static final boolean KEYSTORE_FAILURE_RETURN_VALUE = true;
-
private final String mDataFileName;
/**
- * Construct a new integrity checker to update and check if/when a data file was altered
- * outside expected conditions.
+ * 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 for which integrity check is performed.
+ * @param dataFileName The full path of the data file.
* @throws NullPointerException When data file is empty string.
*/
- public DataIntegrityChecker(@NonNull String dataFileName) {
+ public WifiConfigStoreEncryptionUtil(@NonNull String dataFileName) {
if (TextUtils.isEmpty(dataFileName)) {
throw new NullPointerException("dataFileName must not be null or the empty "
+ "string");
@@ -83,80 +76,16 @@ public class DataIntegrityChecker {
}
/**
- * 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
- * @return Instance of {@link EncryptedData} containing the encrypted integrity data.
- */
- public EncryptedData compute(byte[] data) {
- if (data == null || data.length < 1) {
- reportException(new Exception("No data to compute"), "No data to compute.");
- return null;
- }
- byte[] digest = getDigest(data);
- if (digest == null || digest.length < 1) {
- reportException(new Exception("digest null in compute"),
- "digest null in compute");
- return null;
- }
- EncryptedData integrityData = encrypt(digest, getKeyAlias());
- if (integrityData == null) {
- reportException(new Exception("integrityData null in compute"),
- "integrityData null in compute");
- }
- return integrityData;
- }
-
-
- /**
- * Check the integrity of a given byte array
- *
- * Call this method immediately before trusting the byte array. This method will return false
- * when the integrity data calculated on the byte array does not match the encrypted integrity
- * data provided to compare or if there is an underlying issue with the cryptographic functions
- * or the key store.
+ * Encrypt the provided data blob.
*
- * @param data The data to check if its been altered.
- * @param integrityData Encrypted integrity data to be used for comparison.
- * @return true if the integrity data computed on |data| matches the provided |integrityData|.
+ * @param data Data blob to be encrypted.
+ * @return Instance of {@link EncryptedData} containing the encrypted info.
*/
- public boolean isOk(@NonNull byte[] data, @NonNull EncryptedData integrityData) {
- if (data == null || data.length < 1) {
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
- byte[] currentDigest = getDigest(data);
- if (currentDigest == null || currentDigest.length < 1) {
- reportException(new Exception("current digest null"), "current digest null");
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
- if (integrityData == null) {
- reportException(new Exception("integrityData null in isOk"),
- "integrityData null in isOk");
- return KEYSTORE_FAILURE_RETURN_VALUE;
- }
- byte[] storedDigest = decrypt(integrityData, getKeyAlias());
- 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) {
+ public @Nullable EncryptedData encrypt(byte[] data) {
EncryptedData encryptedData = null;
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- SecretKey secretKeyReference = getOrCreateSecretKey(keyAlias);
+ SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias());
if (secretKeyReference != null) {
cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV());
@@ -178,12 +107,18 @@ public class DataIntegrityChecker {
return encryptedData;
}
- private byte[] decrypt(EncryptedData encryptedData, String keyAlias) {
+ /**
+ * 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(keyAlias);
+ SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias());
if (secretKeyReference != null) {
cipher.init(Cipher.DECRYPT_MODE, secretKeyReference, spec);
decryptedData = cipher.doFinal(encryptedData.getEncryptedData());
@@ -207,8 +142,7 @@ public class DataIntegrityChecker {
private SecretKey getOrCreateSecretKey(String keyAlias) {
SecretKey secretKey = null;
try {
- KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
- keyStore.load(null);
+ 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);
@@ -227,17 +161,15 @@ public class DataIntegrityChecker {
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 (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) {
@@ -250,22 +182,6 @@ public class DataIntegrityChecker {
return secretKey;
}
- 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) {
@@ -275,4 +191,5 @@ public class DataIntegrityChecker {
SystemProperties.set("ctl.start", "bugreport");
}
}
+
}
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
index 188d3b5c7..db0f4289b 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.StaticIpConfiguration;
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 @@ public class XmlUtil {
}
/**
+ * 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 class XmlUtil {
*/
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 @@ public class XmlUtil {
* 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 @@ public class XmlUtil {
* 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 @@ public class XmlUtil {
// 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 class XmlUtil {
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 @@ public class XmlUtil {
/**
* 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 @@ public class XmlUtil {
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);
+ case XML_TAG_ENCRYPTED_DATA:
+ encryptedData = (byte[]) 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 7896a8f0b..afb3ef5f4 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 @@ public class ClientModeImplTest {
@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 @@ public class ClientModeImplTest {
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);
@@ -1047,6 +1049,9 @@ public class ClientModeImplTest {
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>
@@ -1066,15 +1071,15 @@ public class ClientModeImplTest {
mLooper.dispatchAll();
verify(mWifiNative).getEapAnonymousIdentity(any());
- // check that the anonymous identity remains anonymous@<realm> for subsequent connections.
- assertEquals(expectedAnonymousIdentity,
- mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
- // verify that WifiConfigManager#addOrUpdateNetwork() was never called if there is no
- // real pseudonym to be stored. i.e. Encrypted IMSI will be always used
+
+ // 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, never()).addOrUpdateNetwork(any(), anyInt());
+ verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt());
}
/**
@@ -1127,6 +1132,55 @@ public class ClientModeImplTest {
}
/**
+ * 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
@@ -1789,10 +1843,10 @@ public class ClientModeImplTest {
/** 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));
@@ -1866,15 +1920,15 @@ public class ClientModeImplTest {
@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();
}
@@ -1902,16 +1956,17 @@ public class ClientModeImplTest {
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();
}
@@ -2748,6 +2803,59 @@ public class ClientModeImplTest {
}
/**
+ * 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
@@ -3544,14 +3652,15 @@ public class ClientModeImplTest {
@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 000000000..8bf07b8c3
--- /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 702aa99df..17b9d1c2e 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 android.util.Xml;
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 @@ public class DeletedEphemeralSsidsStoreDataTest {
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 @@ public class DeletedEphemeralSsidsStoreDataTest {
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/MacAddressUtilTest.java b/tests/wifitests/src/com/android/server/wifi/MacAddressUtilTest.java
new file mode 100644
index 000000000..7e598db31
--- /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 7336c4119..20b6c4f76 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 android.util.Xml;
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 @@ public class NetworkListStoreDataTest {
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 @@ public class NetworkListStoreDataTest {
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 f40f71bcf..c0f03505e 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 androidx.test.filters.SmallTest;
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 @@ public class NetworkRequestStoreDataTest {
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 @@ public class NetworkRequestStoreDataTest {
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 5c1dcb459..a35c510fc 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 androidx.test.filters.SmallTest;
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 @@ public class NetworkSuggestionStoreDataTest {
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 @@ public class NetworkSuggestionStoreDataTest {
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 4df560fd2..cdd4e6c84 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 android.util.Xml;
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 @@ public class RandomizedMacStoreDataTest {
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 @@ public class RandomizedMacStoreDataTest {
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/SsidSetStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java
index ac6ae21a2..feedc0d2a 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.assertEquals;
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 android.util.Xml;
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 @@ public class SsidSetStoreDataTest {
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 @@ public class SsidSetStoreDataTest {
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 c814aef1a..df93eb4fb 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.assertEquals;
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 android.util.Xml;
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 @@ public class WakeupConfigStoreDataTest {
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 @@ public class WakeupConfigStoreDataTest {
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 @@ public class WakeupConfigStoreDataTest {
*/
@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 009429b3f..a004995b9 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.any;
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 android.provider.Settings;
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 @@ public class WakeupControllerTest {
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 0badc6fbd..6fa1868cb 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.Process;
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.PrintWriter;
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 @@ public class WifiConfigManagerTest {
@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;
@@ -169,6 +175,8 @@ public class WifiConfigManagerTest {
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);
@@ -216,6 +224,10 @@ public class WifiConfigManagerTest {
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 =
@@ -231,13 +243,12 @@ public class WifiConfigManagerTest {
// 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());
}
/**
@@ -292,6 +303,30 @@ public class WifiConfigManagerTest {
}
/**
+ * Verify that a randomized MAC address is generated even if the KeyStore operation fails.
+ */
+ @Test
+ public void testRandomizedMacIsGeneratedEvenIfKeyStoreFails() {
+ when(mMacAddressUtil.calculatePersistentMacForConfiguration(any(), any())).thenReturn(null);
+
+ // Try adding a network.
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+
+ // 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());
+ }
+
+ /**
* Verifies the addition of a single network using
* {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
*/
@@ -4620,7 +4655,7 @@ public class WifiConfigManagerTest {
mWifiPermissionsUtil, mWifiPermissionsWrapper, mWifiInjector,
mNetworkListSharedStoreData, mNetworkListUserStoreData,
mDeletedEphemeralSsidsStoreData, mRandomizedMacStoreData,
- mFrameworkFacade, mLooper.getLooper());
+ mFrameworkFacade, mLooper.getLooper(), mDeviceConfigFacade);
mWifiConfigManager.enableVerboseLogging(1);
}
@@ -5349,4 +5384,32 @@ public class WifiConfigManagerTest {
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 b59e367dd..efa2d4336 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -16,12 +16,9 @@
package com.android.server.wifi;
-import static com.android.server.wifi.WifiConfigStore.ZEROED_ENCRYPTED_DATA;
-
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
-import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -34,8 +31,8 @@ import androidx.test.filters.SmallTest;
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.DataIntegrityChecker;
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;
@@ -43,7 +40,6 @@ import libcore.util.HexEncoding;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
@@ -73,13 +69,7 @@ public class WifiConfigStoreTest {
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=\"2\" />\n"
- + "<Integrity>\n"
- + "<byte-array name=\"EncryptedData\" num=\"48\">000000000000000000000000000000"
- + "000000000000000000000000000000000000000000000000000000000000000000"
- + "</byte-array>\n"
- + "<byte-array name=\"IV\" num=\"12\">000000000000000000000000</byte-array>\n"
- + "</Integrity>\n"
+ + "<int name=\"Version\" value=\"3\" />\n"
+ "<NetworkList>\n"
+ "<Network>\n"
+ "<WifiConfiguration>\n"
@@ -172,7 +162,7 @@ public class WifiConfigStoreTest {
private TestLooper mLooper;
@Mock private Clock mClock;
@Mock private WifiMetrics mWifiMetrics;
- @Mock private DataIntegrityChecker mDataIntegrityChecker;
+ @Mock private WifiConfigStoreEncryptionUtil mEncryptionUtil;
private MockStoreFile mSharedStore;
private MockStoreFile mUserStore;
private MockStoreFile mUserNetworkSuggestionsStore;
@@ -196,10 +186,10 @@ public class WifiConfigStoreTest {
.thenReturn(mAlarmManager.getAlarmManager());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
- when(mDataIntegrityChecker.compute(any(byte[].class)))
- .thenReturn(ZEROED_ENCRYPTED_DATA);
- when(mDataIntegrityChecker.isOk(any(byte[].class), any(EncryptedData.class)))
- .thenReturn(true);
+ 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 =
@@ -432,9 +422,9 @@ public class WifiConfigStoreTest {
// 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());
}
/**
@@ -462,9 +452,9 @@ public class WifiConfigStoreTest {
// 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());
}
/**
@@ -639,9 +629,9 @@ public class WifiConfigStoreTest {
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.
@@ -655,9 +645,9 @@ public class WifiConfigStoreTest {
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.
@@ -671,10 +661,10 @@ public class WifiConfigStoreTest {
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.
@@ -689,10 +679,10 @@ public class WifiConfigStoreTest {
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);
}
@@ -739,9 +729,9 @@ public class WifiConfigStoreTest {
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());
}
/**
@@ -815,190 +805,21 @@ public class WifiConfigStoreTest {
// 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());
- verify(userStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
-
- // We shouldn't perform any data integrity checks on version 1 file.
- verifyZeroInteractions(mDataIntegrityChecker);
- }
-
- /**
- * Tests the read API behaviour when integrity check fails.
- * Expected behaviour: The read should return an empty store data.
- */
- @Test
- public void testReadWhenIntegrityCheckFails() 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_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- TEST_USER_DATA).getBytes());
- mSharedStore.storeRawDataToWrite(
- String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- 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());
- verify(userStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
-
- // Read and verify the data content in the store file (metadata stripped out) has not been
- // sent to the corresponding store data when integrity check fails.
- when(mDataIntegrityChecker.isOk(any(byte[].class), any(EncryptedData.class)))
- .thenReturn(false);
- mWifiConfigStore.read();
- verify(sharedStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
- verify(userStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
+ 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 write API behaviour when integrity check fails.
- * Expected behaviour: The read should return an empty store data.
+ * Tests the read API behaviour to ensure that the integrity data is parsed from the file.
*/
@Test
- public void testWriteWhenIntegrityComputeFails() 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);
- 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);
-
- // Reset store file contents & ensure that the user and store data files are empty.
- mUserStore.storeRawDataToWrite(null);
- mSharedStore.storeRawDataToWrite(null);
- assertNull(mUserStore.getStoreBytes());
- assertNull(mSharedStore.getStoreBytes());
-
- // Write and verify that the data is written to the config store file when integrity
- // computation passes.
- mWifiConfigStore.write(true);
- assertNotNull(mUserStore.getStoreBytes());
- assertNotNull(mSharedStore.getStoreBytes());
- assertTrue(new String(mUserStore.getStoreBytes()).contains(TEST_USER_DATA));
- assertTrue(new String(mSharedStore.getStoreBytes()).contains(TEST_SHARE_DATA));
-
- // Reset store file contents & ensure that the user and store data files are empty.
- mUserStore.storeRawDataToWrite(null);
- mSharedStore.storeRawDataToWrite(null);
- assertNull(mUserStore.getStoreBytes());
- assertNull(mSharedStore.getStoreBytes());
-
- // Write and verify that the data is not written to the config store file when integrity
- // computation fails.
- when(mDataIntegrityChecker.compute(any(byte[].class))).thenReturn(null);
- mWifiConfigStore.write(true);
- assertNull(mUserStore.getStoreBytes());
- assertNull(mSharedStore.getStoreBytes());
- }
-
- /**
- * Tests the write API behaviour to ensure that the integrity data is written to the file.
- */
- @Test
- public void testWriteContainsIntegrityData() throws Exception {
- byte[] encryptedData = new byte[EncryptedData.ENCRYPTED_DATA_LENGTH];
- byte[] iv = new byte[EncryptedData.IV_LENGTH];
- Random random = new Random();
- random.nextBytes(encryptedData);
- random.nextBytes(iv);
- final EncryptedData testEncryptedData = new EncryptedData(encryptedData, iv);
-
- doAnswer(new AnswerWithArguments() {
- public EncryptedData answer(byte[] data) {
- String storeXmlString = new String(data);
- // Verify that we fill in zeros to the data when we compute integrity.
- if (storeXmlString.contains(TEST_SHARE_DATA)) {
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- TEST_SHARE_DATA), storeXmlString);
- } else if (storeXmlString.contains(TEST_USER_DATA)) {
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- TEST_USER_DATA), storeXmlString);
- }
- return testEncryptedData;
- }
- }).when(mDataIntegrityChecker).compute(any(byte[].class));
-
- // 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);
-
- // Write and verify that the data is written to the config store file when integrity
- // computation passes.
- mWifiConfigStore.write(true);
-
- // Verify that we fill in zeros to the data when we computed integrity.
- verify(mDataIntegrityChecker, times(2)).compute(any(byte[].class));
-
- // Verify the parsed integrity data
- assertNotNull(mUserStore.getStoreBytes());
- assertNotNull(mSharedStore.getStoreBytes());
- String userStoreXmlString = new String(mUserStore.getStoreBytes());
- String sharedStoreXmlString = new String(mSharedStore.getStoreBytes());
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(encryptedData).toLowerCase(),
- HexEncoding.encodeToString(iv).toLowerCase(),
- TEST_USER_DATA), userStoreXmlString);
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(encryptedData).toLowerCase(),
- HexEncoding.encodeToString(iv).toLowerCase(),
- TEST_SHARE_DATA), sharedStoreXmlString);
- }
-
- /**
- * Tests the read API behaviour to ensure that the integrity data is parsed from the file and
- * used for checking integrity of the file.
- */
- @Test
- public void testReadParsesIntegrityData() throws Exception {
- byte[] encryptedData = new byte[EncryptedData.ENCRYPTED_DATA_LENGTH];
- byte[] iv = new byte[EncryptedData.IV_LENGTH];
+ 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);
@@ -1033,40 +854,14 @@ public class WifiConfigStoreTest {
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.
+ // to the corresponding store data.
mWifiConfigStore.read();
- verify(sharedStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
- verify(userStoreData, times(1)).deserializeData(any(XmlPullParser.class), anyInt());
-
- // Verify that we parsed the integrity data and used it for checking integrity of the file.
- ArgumentCaptor<EncryptedData> integrityCaptor =
- ArgumentCaptor.forClass(EncryptedData.class);
- ArgumentCaptor<byte[]> dataCaptor = ArgumentCaptor.forClass(byte[].class);
- // Will be invoked twice for each file - shared & user store file.
- verify(mDataIntegrityChecker, times(2)).isOk(
- dataCaptor.capture(), integrityCaptor.capture());
- // Verify the parsed integrity data
- assertEquals(2, integrityCaptor.getAllValues().size());
- EncryptedData parsedEncryptedData1 = integrityCaptor.getAllValues().get(0);
- assertArrayEquals(encryptedData, parsedEncryptedData1.getEncryptedData());
- assertArrayEquals(iv, parsedEncryptedData1.getIv());
- EncryptedData parsedEncryptedData2 = integrityCaptor.getAllValues().get(1);
- assertArrayEquals(encryptedData, parsedEncryptedData2.getEncryptedData());
- assertArrayEquals(iv, parsedEncryptedData2.getIv());
-
- // Verify that we fill in zeros to the data when we performed integrity checked.
- assertEquals(2, dataCaptor.getAllValues().size());
- String sharedStoreXmlStringWithZeroedIntegrity =
- new String(dataCaptor.getAllValues().get(0));
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- TEST_SHARE_DATA), sharedStoreXmlStringWithZeroedIntegrity);
- String userStoreXmlStringWithZeroedIntegrity = new String(dataCaptor.getAllValues().get(1));
- assertEquals(String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getEncryptedData()),
- HexEncoding.encodeToString(ZEROED_ENCRYPTED_DATA.getIv()),
- TEST_USER_DATA), userStoreXmlStringWithZeroedIntegrity);
+ 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());
}
/**
@@ -1078,7 +873,7 @@ public class WifiConfigStoreTest {
private boolean mStoreWritten;
MockStoreFile(@WifiConfigStore.StoreFileId int fileId) {
- super(new File("MockStoreFile"), fileId, mDataIntegrityChecker);
+ super(new File("MockStoreFile"), fileId, mEncryptionUtil);
}
@Override
@@ -1129,13 +924,14 @@ public class WifiConfigStoreTest {
}
@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/WifiLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
index a40de55e9..255073b8b 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 @@ public class WifiLockManagerTest {
}
/**
- * 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 7e086d4ce..73ac30f41 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -2999,9 +2999,10 @@ public class WifiMetricsTest {
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 63cc8bf60..d20c99c4f 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -68,6 +68,7 @@ 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;
@@ -84,6 +85,8 @@ 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;
@@ -184,6 +187,7 @@ public class WifiNetworkFactoryTest {
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,
@@ -951,16 +955,11 @@ public class WifiNetworkFactoryTest {
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());
}
/**
@@ -996,13 +995,8 @@ public class WifiNetworkFactoryTest {
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());
}
/**
@@ -1615,9 +1609,17 @@ public class WifiNetworkFactoryTest {
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);
}
@@ -1676,6 +1678,7 @@ public class WifiNetworkFactoryTest {
mLooper.dispatchAll();
verify(mNetworkRequestMatchCallback).onAbort();
+ verify(mWifiScanner, times(2)).getSingleScanResults();
verify(mWifiScanner, times(2)).startScan(any(), any(), any());
verifyUnfullfillableDispatched(mConnectivityMessenger);
@@ -1717,6 +1720,7 @@ public class WifiNetworkFactoryTest {
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);
@@ -1751,6 +1755,7 @@ public class WifiNetworkFactoryTest {
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());
@@ -1790,6 +1795,7 @@ public class WifiNetworkFactoryTest {
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();
@@ -2082,10 +2088,10 @@ public class WifiNetworkFactoryTest {
/**
* 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();
@@ -2105,6 +2111,9 @@ public class WifiNetworkFactoryTest {
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.
@@ -2126,8 +2135,7 @@ public class WifiNetworkFactoryTest {
* 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();
@@ -2171,7 +2179,7 @@ public class WifiNetworkFactoryTest {
* not approved previously.
*/
@Test
- public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchNotPreviouslyApproved()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidAndBssidMatchNotApproved()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2212,7 +2220,7 @@ public class WifiNetworkFactoryTest {
* (not access point) that was approved previously.
*/
@Test
- public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchPreviouslyApproved()
+ public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchApproved()
throws Exception {
// 1. First request (no user approval bypass)
sendNetworkRequestAndSetupForConnectionStatus();
@@ -2404,7 +2412,7 @@ public class WifiNetworkFactoryTest {
* Verify the config store save and load could preserve the elements order.
*/
@Test
- public void testStoteConfigSaveAndLoadPreserveOrder() throws Exception {
+ 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));
@@ -2429,6 +2437,159 @@ public class WifiNetworkFactoryTest {
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);
}
@@ -2498,6 +2659,8 @@ public class WifiNetworkFactoryTest {
mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+ validateUiStartParams(true);
+
mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
TEST_CALLBACK_IDENTIFIER);
verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
@@ -2505,7 +2668,7 @@ public class WifiNetworkFactoryTest {
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.
@@ -2520,6 +2683,10 @@ public class WifiNetworkFactoryTest {
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];
@@ -2697,7 +2864,7 @@ public class WifiNetworkFactoryTest {
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);
@@ -2766,7 +2933,7 @@ public class WifiNetworkFactoryTest {
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();
}
@@ -2781,6 +2948,8 @@ public class WifiNetworkFactoryTest {
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/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index 9a3bd75be..d5f7dd981 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 @@ public class WifiServiceImplTest {
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),
@@ -2788,77 +2789,57 @@ public class WifiServiceImplTest {
}
/**
- * 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.
+ * Verify the call to getPasspointConfigurations when the caller doesn't have
+ * NETWORK_SETTINGS and NETWORK_SETUP_WIZARD permissions.
*/
- @Test(expected = SecurityException.class)
- public void testGetPasspointConfigurationsWithOutPermissions() {
+ @Test
+ public void testGetPasspointConfigurationsWithOutPrivilegedPermissions() {
when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false);
mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME);
+
+ verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(false));
}
/**
- * Verify that getPasspointConfigurations called by apps that has invalid package will
- * throw {@link SecurityException}.
+ * Verify that the call to getPasspointConfigurations when the caller does have
+ * NETWORK_SETTINGS permission.
*/
- @Test(expected = SecurityException.class)
- public void testGetPasspointConfigurationWithInvalidPackage() {
- doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(),
- eq(TEST_PACKAGE_NAME));
+ @Test
+ public void testGetPasspointConfigurationsWithPrivilegedPermissions() {
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.
- */
- @Test
- public void testGetPasspointConfigurationForAppsTargetingBelowQSDK() {
- when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
- when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false);
- when(mWifiPermissionsUtil.isTargetSdkLessThan(eq(TEST_PACKAGE_NAME),
- eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
-
- List<PasspointConfiguration> result = mWifiServiceImpl.getPasspointConfigurations(
- TEST_PACKAGE_NAME);
- assertNotNull(result);
- assertEquals(0, result.size());
+ verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(true));
}
/**
- * Verify that the call to removePasspointConfiguration is not redirected to specific API
- * syncRemovePasspointConfig when the caller doesn't have NETWORK_SETTINGS and
- * NETWORK_CARRIER_PROVISIONING permission.
+ * Verify the call to removePasspointConfigurations when the caller doesn't have
+ * NETWORK_SETTINGS and NETWORK_CARRIER_PROVISIONING permissions.
*/
- @Test(expected = SecurityException.class)
- public void testRemovePasspointConfigurationWithOutPermissions() {
+ @Test
+ public void testRemovePasspointConfigurationWithOutPrivilegedPermissions() {
when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn(
false);
- mWifiServiceImpl.removePasspointConfiguration(null, null);
+ mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME);
+ verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(false), eq(TEST_FQDN));
}
/**
- * 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.
+ * Verify the call to removePasspointConfigurations when the caller does have
+ * 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), anyInt())).thenReturn(true);
+ true);
- assertFalse(mWifiServiceImpl.removePasspointConfiguration(null, null));
+ mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME);
+ verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(true), eq(TEST_FQDN));
}
/**
@@ -3449,6 +3430,25 @@ public class WifiServiceImplTest {
}
/**
+ * 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
@@ -3650,13 +3650,14 @@ public class WifiServiceImplTest {
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();
@@ -3679,8 +3680,9 @@ public class WifiServiceImplTest {
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();
@@ -3703,7 +3705,7 @@ public class WifiServiceImplTest {
} catch (SecurityException e) {
}
verify(mClientModeImpl, never()).syncGetConfiguredNetworks(anyInt(), any(), anyInt());
- verify(mClientModeImpl, never()).syncGetPasspointConfigs(any());
+ verify(mClientModeImpl, never()).syncGetPasspointConfigs(any(), eq(false));
}
/**
@@ -4269,4 +4271,17 @@ public class WifiServiceImplTest {
} 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 aa44023ae..c33a4d55e 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 @@ public class WifiVendorHalTest {
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 @@ public class WifiVendorHalTest {
| 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 @@ public class WifiVendorHalTest {
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 @@ public class WifiVendorHalTest {
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
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 c76e2c878..7a815001c 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 androidx.test.filters.SmallTest;
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 @@ public class PasspointConfigSharedStoreDataTest {
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 @@ public class PasspointConfigSharedStoreDataTest {
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 82cdb5a90..5278e1933 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.internal.util.FastXmlSerializer;
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 @@ public class PasspointConfigUserStoreDataTest {
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 @@ public class PasspointConfigUserStoreDataTest {
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 338f7bc40..131425af8 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -268,7 +268,8 @@ public class PasspointManagerTest {
* @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));
}
@@ -283,6 +284,7 @@ public class PasspointManagerTest {
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;
}
@@ -651,14 +653,14 @@ public class PasspointManagerTest {
// 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());
@@ -694,13 +696,13 @@ public class PasspointManagerTest {
// 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());
@@ -824,7 +826,7 @@ public class PasspointManagerTest {
*/
@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();
}
@@ -1285,8 +1287,8 @@ public class PasspointManagerTest {
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));
}
/**
@@ -1800,7 +1802,7 @@ public class PasspointManagerTest {
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);
@@ -1814,6 +1816,45 @@ public class PasspointManagerTest {
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/p2p/WifiP2pServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
index 15d9ef915..1091c1dac 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 @@ public class WifiP2pServiceImplTest {
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 d2f22da6a..bd0ad321d 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.WifiStatus;
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 @@ public class RttNativeTest {
}
}
+ /**
+ * 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 c281b6440..000000000
--- a/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
+++ /dev/null
@@ -1,69 +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;
-
-/**
- * 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());
- EncryptedData encryptedData = dataIntegrityChecker.compute(sGoodData);
- assertTrue(dataIntegrityChecker.isOk(sGoodData, encryptedData));
- }
-
- /**
- * 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());
- EncryptedData encryptedData = dataIntegrityChecker.compute(sGoodData);
- assertFalse(dataIntegrityChecker.isOk(sBadData, encryptedData));
- }
-}
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 45adffd1d..266a2ce23 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 @@ public class ScanResultUtilTest {
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/XmlUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
index 85b4a9370..8f96bc106 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.NetworkSelectionStatusXmlUtil;
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 @@ public class XmlUtilTest {
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 @@ public class XmlUtilTest {
}
/**
+ * 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 @@ public class XmlUtilTest {
}
/**
+ * 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 @@ public class XmlUtilTest {
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 @@ public class XmlUtilTest {
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 @@ public class XmlUtilTest {
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 @@ public class XmlUtilTest {
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)