summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-12-11 02:14:23 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-12-11 02:14:23 +0000
commit8b0bd8604e03b63cad9e67d8fe612ca912ad70d6 (patch)
tree7fd89e447f0618ebd9e8ae1d34e14724d5f8d964
parent47656d3e7699d71637494fbc01e493babf354a51 (diff)
parent491c22553879a8d56d761c218177dec6afa80aa9 (diff)
downloadandroid_frameworks_opt_net_wifi-8b0bd8604e03b63cad9e67d8fe612ca912ad70d6.tar.gz
android_frameworks_opt_net_wifi-8b0bd8604e03b63cad9e67d8fe612ca912ad70d6.tar.bz2
android_frameworks_opt_net_wifi-8b0bd8604e03b63cad9e67d8fe612ca912ad70d6.zip
Snap for 6063219 from 491c22553879a8d56d761c218177dec6afa80aa9 to qt-qpr2-release
Change-Id: I25d99901bbe78c1ce402481a4d98a90f6bf600ff
-rw-r--r--service/java/com/android/server/wifi/ClientModeImpl.java14
-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/DeviceConfigFacade.java21
-rw-r--r--service/java/com/android/server/wifi/FrameworkFacade.java21
-rw-r--r--service/java/com/android/server/wifi/WifiConfigManager.java26
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java29
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java42
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ConnectionFailureNotifierTest.java184
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java39
10 files changed, 656 insertions, 5 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 119d6d0e8..5cf909695 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -763,6 +763,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.
@@ -816,7 +817,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();
@@ -3158,6 +3160,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) {
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/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 4fbe31867..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;
@@ -227,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/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index a246a86f7..80b583feb 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -321,6 +321,7 @@ public class WifiConfigManager {
private final int mMaxNumActiveChannelsForPartialScans;
private final FrameworkFacade mFrameworkFacade;
+ private final DeviceConfigFacade mDeviceConfigFacade;
/**
* Verbose logging flag. Toggled by developer options.
@@ -378,6 +379,7 @@ public class WifiConfigManager {
private boolean mPnoFrequencyCullingEnabled = false;
private boolean mPnoRecencySortingEnabled = false;
+ private Set<String> mRandomizationFlakySsidHotlist;
@@ -395,7 +397,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;
@@ -447,6 +450,14 @@ 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);
@@ -1522,6 +1533,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) {
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 83ae89cdc..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;
@@ -155,6 +156,7 @@ public class WifiInjector {
private final IpMemoryStore mIpMemoryStore;
private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
private final MacAddressUtil mMacAddressUtil;
+ private final ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
public WifiInjector(Context context) {
if (context == null) {
@@ -172,6 +174,8 @@ public class WifiInjector {
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);
@@ -253,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);
@@ -623,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.
*/
@@ -701,6 +716,14 @@ public class WifiInjector {
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.
*/
@@ -759,4 +782,8 @@ public class WifiInjector {
public HostapdHal getHostapdHal() {
return mHostapdHal;
}
+
+ public String getWifiStackPackageName() {
+ return mContext.getPackageName();
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index 9eb88b637..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);
@@ -2816,6 +2818,44 @@ public class ClientModeImplTest {
}
/**
+ * 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
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/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 7dd675c3c..23eea328f 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;
@@ -136,7 +138,10 @@ public class WifiConfigManagerTest {
@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;
@@ -170,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);
@@ -240,6 +247,8 @@ public class WifiConfigManagerTest {
.startMocking();
when(WifiConfigStore.createUserFiles(anyInt(), anyBoolean())).thenReturn(mock(List.class));
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager);
+ verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(),
+ mOnPropertiesChangedListenerCaptor.capture());
}
/**
@@ -4632,7 +4641,7 @@ public class WifiConfigManagerTest {
mWifiPermissionsUtil, mWifiPermissionsWrapper, mWifiInjector,
mNetworkListSharedStoreData, mNetworkListUserStoreData,
mDeletedEphemeralSsidsStoreData, mRandomizedMacStoreData,
- mFrameworkFacade, mLooper.getLooper());
+ mFrameworkFacade, mLooper.getLooper(), mDeviceConfigFacade);
mWifiConfigManager.enableVerboseLogging(1);
}
@@ -5361,4 +5370,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));
+ }
}