CONFIGURED_NETWORKS_CHANGED_ACTION: stop sending WifiConfiguration and require ACCESS_WIFI_STATE permission

WifiConfiguration contains sensitive location
information. Stop sending this information in the
broadcast intent. Also require receivers to have
the ACCESS_WIFI_STATE permission.

Bug: 158874479
Test: Add logs locally in Settings to verify broadcast is received.
Test: Verify Settings still works correctly.
Test: atest FrameworksWifiTests
Change-Id: I657063f68701d57cfeb3765dfbab25ba50ef7b97
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 6b88f71..99de927 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -18,6 +18,7 @@
 
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLE_REASON_INFOS;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -834,35 +835,23 @@
     }
 
     /**
-     * Method to send out the configured networks change broadcast when a single network
-     * configuration is changed.
+     * Method to send out the configured networks change broadcast when network configurations
+     * changed.
      *
-     * @param network WifiConfiguration corresponding to the network that was changed.
+     * In Android R we stopped sending out WifiConfiguration due to user privacy concerns.
+     * Thus, no matter how many networks changed,
+     * {@link WifiManager#EXTRA_MULTIPLE_NETWORKS_CHANGED} is always set to true, and
+     * {@link WifiManager#EXTRA_WIFI_CONFIGURATION} is always null.
+     *
      * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
      *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
      */
-    private void sendConfiguredNetworkChangedBroadcast(
-            WifiConfiguration network, int reason) {
-        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
-        // Create a new WifiConfiguration with passwords masked before we send it out.
-        WifiConfiguration broadcastNetwork = new WifiConfiguration(network);
-        maskPasswordsInWifiConfiguration(broadcastNetwork);
-        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork);
-        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
-    /**
-     * Method to send out the configured networks change broadcast when multiple network
-     * configurations are changed.
-     */
-    private void sendConfiguredNetworksChangedBroadcast() {
+    private void sendConfiguredNetworkChangedBroadcast(int reason) {
         Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.ACCESS_WIFI_STATE);
     }
 
     /**
@@ -1376,7 +1365,6 @@
         }
         WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
         sendConfiguredNetworkChangedBroadcast(
-                newConfig,
                 result.isNewNetwork()
                         ? WifiManager.CHANGE_REASON_ADDED
                         : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
@@ -1475,7 +1463,7 @@
         if (!config.ephemeral && !config.isPasspoint()) {
             mLruConnectionTracker.removeNetwork(config);
         }
-        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+        sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED);
         // Unless the removed network is ephemeral or Passpoint, persist the network removal.
         if (!config.ephemeral && !config.isPasspoint()) {
             saveToStore(true);
@@ -1688,7 +1676,7 @@
      */
     private void setNetworkStatus(WifiConfiguration config, int status) {
         config.status = status;
-        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+        sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
     }
 
     /**
@@ -1949,7 +1937,7 @@
             removeConnectChoiceFromAllNetworks(config.getKey());
             clearNetworkConnectChoice(config.networkId);
         }
-        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+        sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
         if (!config.ephemeral) {
             saveToStore(true);
         }
@@ -3056,7 +3044,7 @@
         // on load (i.e. boot) so that if the user changed SIMs while the device was powered off,
         // we do not reuse stale credentials that would lead to authentication failure.
         resetSimNetworks();
-        sendConfiguredNetworksChangedBroadcast();
+        sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED);
         mPendingStoreRead = false;
     }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index dcd0fa8..815b3ad 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -204,9 +204,6 @@
 
         when(mContext.getSystemService(ActivityManager.class))
                 .thenReturn(mock(ActivityManager.class));
-        Context mockContext = mock(Context.class);
-        PackageManager mockPackageManager = mock(PackageManager.class);
-        when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
 
         when(mWifiKeyStore
                 .updateNetworkKeys(any(WifiConfiguration.class), any()))
@@ -418,8 +415,8 @@
         assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
         assertTrue(result.isNewNetwork());
 
-        verifyNetworkRemoveBroadcast(ephemeralNetwork);
-        verifyNetworkAddBroadcast(openNetwork);
+        verifyNetworkRemoveBroadcast();
+        verifyNetworkAddBroadcast();
 
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(openNetwork);
@@ -449,7 +446,7 @@
 
         NetworkUpdateResult result = addNetworkToWifiConfigManager(ephemeralNetwork2);
         assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
-        verifyNetworkUpdateBroadcast(ephemeralNetwork);
+        verifyNetworkUpdateBroadcast();
 
         // Ensure that the write was not invoked for ephemeral network addition.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
@@ -1161,7 +1158,7 @@
 
         // Verify keys are not being removed.
         verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class));
-        verifyNetworkRemoveBroadcast(passpointNetwork);
+        verifyNetworkRemoveBroadcast();
         // Ensure that the write was not invoked for Passpoint network remove.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
 
@@ -1723,7 +1720,7 @@
         assertFalse(result.isNewNetwork());
 
         // Verify no changes to the original network configuration.
-        verifyNetworkUpdateBroadcast(originalNetwork);
+        verifyNetworkUpdateBroadcast();
         verifyNetworkInConfigStoreData(originalNetwork);
         assertFalse(result.hasIpChanged());
         assertFalse(result.hasProxyChanged());
@@ -4911,57 +4908,55 @@
      * Verifies that the network was present in the network change broadcast and returns the
      * change reason.
      */
-    private int verifyNetworkInBroadcastAndReturnReason(WifiConfiguration configuration) {
+    private int verifyNetworkInBroadcastAndReturnReason() {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(UserHandle.class);
-        mContextConfigStoreMockOrder.verify(mContext)
-                .sendBroadcastAsUser(intentCaptor.capture(), userHandleCaptor.capture());
+        mContextConfigStoreMockOrder.verify(mContext).sendBroadcastAsUser(
+                intentCaptor.capture(),
+                eq(UserHandle.ALL),
+                eq(android.Manifest.permission.ACCESS_WIFI_STATE));
 
-        assertEquals(userHandleCaptor.getValue(), UserHandle.ALL);
         Intent intent = intentCaptor.getValue();
 
-        int changeReason = intent.getIntExtra(WifiManager.EXTRA_CHANGE_REASON, -1);
         WifiConfiguration retrievedConfig =
                 (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
-        assertEquals(retrievedConfig.getKey(), configuration.getKey());
+        assertNull(retrievedConfig);
 
-        // Verify that all the passwords are masked in the broadcast configuration.
-        assertPasswordsMaskedInWifiConfiguration(retrievedConfig);
-
-        return changeReason;
+        return intent.getIntExtra(WifiManager.EXTRA_CHANGE_REASON, -1);
     }
 
     /**
      * Verifies that we sent out an add broadcast with the provided network.
      */
-    private void verifyNetworkAddBroadcast(WifiConfiguration configuration) {
+    private void verifyNetworkAddBroadcast() {
         assertEquals(
-                verifyNetworkInBroadcastAndReturnReason(configuration),
+                verifyNetworkInBroadcastAndReturnReason(),
                 WifiManager.CHANGE_REASON_ADDED);
     }
 
     /**
      * Verifies that we sent out an update broadcast with the provided network.
      */
-    private void verifyNetworkUpdateBroadcast(WifiConfiguration configuration) {
+    private void verifyNetworkUpdateBroadcast() {
         assertEquals(
-                verifyNetworkInBroadcastAndReturnReason(configuration),
+                verifyNetworkInBroadcastAndReturnReason(),
                 WifiManager.CHANGE_REASON_CONFIG_CHANGE);
     }
 
     /**
      * Verifies that we sent out a remove broadcast with the provided network.
      */
-    private void verifyNetworkRemoveBroadcast(WifiConfiguration configuration) {
+    private void verifyNetworkRemoveBroadcast() {
         assertEquals(
-                verifyNetworkInBroadcastAndReturnReason(configuration),
+                verifyNetworkInBroadcastAndReturnReason(),
                 WifiManager.CHANGE_REASON_REMOVED);
     }
 
     private void verifyWifiConfigStoreRead() {
         assertTrue(mWifiConfigManager.loadFromStore());
-        mContextConfigStoreMockOrder.verify(mContext)
-                .sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+        mContextConfigStoreMockOrder.verify(mContext).sendBroadcastAsUser(
+                any(Intent.class),
+                any(UserHandle.class),
+                eq(android.Manifest.permission.ACCESS_WIFI_STATE));
     }
 
     private void triggerStoreReadIfNeeded() {
@@ -5022,7 +5017,7 @@
         assertTrue(result.hasIpChanged());
         assertTrue(result.hasProxyChanged());
 
-        verifyNetworkAddBroadcast(configuration);
+        verifyNetworkAddBroadcast();
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(configuration);
         return result;
@@ -5039,7 +5034,7 @@
         assertTrue(result.hasIpChanged());
         assertTrue(result.hasProxyChanged());
 
-        verifyNetworkAddBroadcast(configuration);
+        verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for ephemeral network addition.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
         return result;
@@ -5057,7 +5052,7 @@
         assertTrue(result.hasIpChanged());
         assertTrue(result.hasProxyChanged());
 
-        verifyNetworkAddBroadcast(configuration);
+        verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for ephemeral network addition.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
         return result;
@@ -5077,7 +5072,7 @@
         // Verify keys are not being installed.
         verify(mWifiKeyStore, never()).updateNetworkKeys(any(WifiConfiguration.class),
                 any(WifiConfiguration.class));
-        verifyNetworkAddBroadcast(configuration);
+        verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for Passpoint network addition.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
         return result;
@@ -5108,7 +5103,7 @@
         assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
         assertFalse(result.isNewNetwork());
 
-        verifyNetworkUpdateBroadcast(configuration);
+        verifyNetworkUpdateBroadcast();
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(configuration);
         return result;
@@ -5146,7 +5141,7 @@
         assertTrue(mWifiConfigManager.removeNetwork(
                 configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME));
 
-        verifyNetworkRemoveBroadcast(configuration);
+        verifyNetworkRemoveBroadcast();
         // Verify if the config store write was triggered without this new configuration.
         verifyNetworkNotInConfigStoreData(configuration);
         verify(mBssidBlocklistMonitor, atLeastOnce()).handleNetworkRemoved(configuration.SSID);
@@ -5160,7 +5155,7 @@
         assertTrue(mWifiConfigManager.removeNetwork(
                 configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME));
 
-        verifyNetworkRemoveBroadcast(configuration);
+        verifyNetworkRemoveBroadcast();
         // Ensure that the write was not invoked for ephemeral network remove.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
     }
@@ -5175,7 +5170,7 @@
 
         // Verify keys are not being removed.
         verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class));
-        verifyNetworkRemoveBroadcast(configuration);
+        verifyNetworkRemoveBroadcast();
         // Ensure that the write was not invoked for Passpoint network remove.
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
     }
@@ -5186,7 +5181,7 @@
      */
     private void verifyUpdateNetworkStatus(WifiConfiguration configuration, int status) {
         assertEquals(status, configuration.status);
-        verifyNetworkUpdateBroadcast(configuration);
+        verifyNetworkUpdateBroadcast();
     }
 
     /**