diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-12-11 02:14:23 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-12-11 02:14:23 +0000 |
commit | 8b0bd8604e03b63cad9e67d8fe612ca912ad70d6 (patch) | |
tree | 7fd89e447f0618ebd9e8ae1d34e14724d5f8d964 | |
parent | 47656d3e7699d71637494fbc01e493babf354a51 (diff) | |
parent | 491c22553879a8d56d761c218177dec6afa80aa9 (diff) | |
download | android_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
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)); + } } |