MAC randomization SSID hotlist support

Reads from DeviceConfig and parses a comma separated of SSIDs.

Bug: 144172117
Test: atest FrameworksWifiTests
Test: Manually tested on device
Change-Id: I53b15e884ecba377277eac81f9e3ca86474f5c13
Merged-In: Ie42ce853810aa5dfd4eef89dff281740dafaf3be
(cherry picked from a522b345fa15716a21fd9edcf523cf3c1331dd94)
diff --git a/service/java/com/android/server/wifi/DeviceConfigFacade.java b/service/java/com/android/server/wifi/DeviceConfigFacade.java
index a9889f4..25cc2f7 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 @@
         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/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index a246a86..80b583f 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -321,6 +321,7 @@
     private final int mMaxNumActiveChannelsForPartialScans;
 
     private final FrameworkFacade mFrameworkFacade;
+    private final DeviceConfigFacade mDeviceConfigFacade;
 
     /**
      * Verbose logging flag. Toggled by developer options.
@@ -378,6 +379,7 @@
 
     private boolean mPnoFrequencyCullingEnabled = false;
     private boolean mPnoRecencySortingEnabled = false;
+    private Set<String> mRandomizationFlakySsidHotlist;
 
 
 
@@ -395,7 +397,8 @@
             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 @@
         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 @@
     }
 
     /**
+     * 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 83ae89c..af8c147 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -253,7 +253,7 @@
                 mWifiPermissionsWrapper, this, new NetworkListSharedStoreData(mContext),
                 new NetworkListUserStoreData(mContext),
                 new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(),
-                mFrameworkFacade, mWifiCoreHandlerThread.getLooper());
+                mFrameworkFacade, mWifiCoreHandlerThread.getLooper(), mDeviceConfigFacade);
         mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
         mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
         mConnectivityLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 7dd675c..23eea32 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -42,6 +42,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.test.TestLooper;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -70,6 +71,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -136,7 +138,10 @@
     @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 @@
                 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 @@
                 .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 @@
                         mWifiPermissionsUtil, mWifiPermissionsWrapper, mWifiInjector,
                         mNetworkListSharedStoreData, mNetworkListUserStoreData,
                         mDeletedEphemeralSsidsStoreData, mRandomizedMacStoreData,
-                        mFrameworkFacade, mLooper.getLooper());
+                        mFrameworkFacade, mLooper.getLooper(), mDeviceConfigFacade);
         mWifiConfigManager.enableVerboseLogging(1);
     }
 
@@ -5361,4 +5370,32 @@
         assertFalse(mWifiConfigManager.getConfiguredNetwork(networkId)
                     .getNetworkSelectionStatus().isNetworkTemporaryDisabled());
     }
+
+    /**
+     * Verifies that isInFlakyRandomizationSsidHotlist returns true if the network's SSID is in
+     * the hotlist and the network is using randomized MAC.
+     */
+    @Test
+    public void testFlakyRandomizationSsidHotlist() {
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+        int networkId = result.getNetworkId();
+
+        // should return false when there is nothing in the hotlist
+        assertFalse(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+
+        // add the network's SSID to the hotlist and verify the method returns true
+        Set<String> ssidHotlist = new HashSet<>();
+        ssidHotlist.add(openNetwork.SSID);
+        when(mDeviceConfigFacade.getRandomizationFlakySsidHotlist()).thenReturn(ssidHotlist);
+        mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null);
+        assertTrue(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+
+        // Now change the macRandomizationSetting to "trusted" and then verify
+        // isInFlakyRandomizationSsidHotlist returns false
+        openNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+        NetworkUpdateResult networkUpdateResult = updateNetworkToWifiConfigManager(openNetwork);
+        assertNotEquals(WifiConfiguration.INVALID_NETWORK_ID, networkUpdateResult.getNetworkId());
+        assertFalse(mWifiConfigManager.isInFlakyRandomizationSsidHotlist(networkId));
+    }
 }