Merge commit '74803dc'

Change-Id: I35a76a27390c75a47e6387fb0ee853a5a689a56f
diff --git a/wifi/java/android/net/wifi/NetworkUpdateResult.java b/wifi/java/android/net/wifi/NetworkUpdateResult.java
index 6b7b68b..234bbe1 100644
--- a/wifi/java/android/net/wifi/NetworkUpdateResult.java
+++ b/wifi/java/android/net/wifi/NetworkUpdateResult.java
@@ -22,6 +22,7 @@
     int netId;
     boolean ipChanged;
     boolean proxyChanged;
+    boolean isNewNetwork = false;
 
     public NetworkUpdateResult(int id) {
         netId = id;
@@ -58,4 +59,12 @@
     public boolean hasProxyChanged() {
         return proxyChanged;
     }
+
+    public boolean isNewNetwork() {
+        return isNewNetwork;
+    }
+
+    public void setIsNewNetwork(boolean isNew) {
+        isNewNetwork = isNew;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 46ad036..5dec269 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -273,7 +273,8 @@
             mConfiguredNetworks.get(netId).status = Status.ENABLED;
         }
         mWifiNative.saveConfig();
-        sendConfiguredNetworksChangedBroadcast();
+        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
+                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
         return result;
     }
 
@@ -307,13 +308,16 @@
     boolean forgetNetwork(int netId) {
         if (mWifiNative.removeNetwork(netId)) {
             mWifiNative.saveConfig();
+            WifiConfiguration target = null;
             WifiConfiguration config = mConfiguredNetworks.get(netId);
             if (config != null) {
-                mConfiguredNetworks.remove(netId);
+                target = mConfiguredNetworks.remove(netId);
                 mNetworkIds.remove(configKey(config));
             }
-            writeIpAndProxyConfigurations();
-            sendConfiguredNetworksChangedBroadcast();
+            if (target != null) {
+                writeIpAndProxyConfigurations();
+                sendConfiguredNetworksChangedBroadcast(target, WifiManager.CHANGE_REASON_REMOVED);
+            }
             return true;
         } else {
             loge("Failed to remove network " + netId);
@@ -332,7 +336,11 @@
      */
     int addOrUpdateNetwork(WifiConfiguration config) {
         NetworkUpdateResult result = addOrUpdateNetworkNative(config);
-        sendConfiguredNetworksChangedBroadcast();
+        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+            sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()),
+                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
+                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+        }
         return result.getNetworkId();
     }
 
@@ -347,14 +355,17 @@
      */
     boolean removeNetwork(int netId) {
         boolean ret = mWifiNative.removeNetwork(netId);
+        WifiConfiguration config = null;
         if (ret) {
-            WifiConfiguration config = mConfiguredNetworks.get(netId);
+            config = mConfiguredNetworks.get(netId);
             if (config != null) {
-                mConfiguredNetworks.remove(netId);
+                config = mConfiguredNetworks.remove(netId);
                 mNetworkIds.remove(configKey(config));
             }
         }
-        sendConfiguredNetworksChangedBroadcast();
+        if (config != null) {
+            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+        }
         return ret;
     }
 
@@ -364,12 +375,24 @@
      * API. The more powerful selectNetwork()/saveNetwork() is used by the
      * state machine for connecting to a network
      *
-     * @param netId network to be removed
+     * @param netId network to be enabled
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean enableNetwork(int netId, boolean disableOthers) {
         boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
-        sendConfiguredNetworksChangedBroadcast();
+        if (disableOthers) {
+            sendConfiguredNetworksChangedBroadcast();
+        } else {
+            WifiConfiguration enabledNetwork = null;
+            synchronized(mConfiguredNetworks) {
+                enabledNetwork = mConfiguredNetworks.get(netId);
+            }
+            // check just in case the network was removed by someone else.
+            if (enabledNetwork != null) {
+                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
+                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+            }
+        }
         return ret;
     }
 
@@ -402,13 +425,18 @@
      */
     boolean disableNetwork(int netId, int reason) {
         boolean ret = mWifiNative.disableNetwork(netId);
+        WifiConfiguration network = null;
         WifiConfiguration config = mConfiguredNetworks.get(netId);
         /* Only change the reason if the network was not previously disabled */
         if (config != null && config.status != Status.DISABLED) {
             config.status = Status.DISABLED;
             config.disableReason = reason;
+            network = config;
         }
-        sendConfiguredNetworksChangedBroadcast();
+        if (network != null) {
+            sendConfiguredNetworksChangedBroadcast(network,
+                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+        }
         return ret;
     }
 
@@ -574,9 +602,29 @@
         return false;
     }
 
+    /**
+     * Should be called when a single network configuration is made.
+     * @param network The network configuration that changed.
+     * @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 sendConfiguredNetworksChangedBroadcast(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);
+        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
+        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Should be called when multiple network configuration changes are made.
+     */
     private void sendConfiguredNetworksChangedBroadcast() {
         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.sendBroadcast(intent);
     }
 
@@ -1135,6 +1183,7 @@
         mNetworkIds.put(configKey(currentConfig), netId);
 
         NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
+        result.setIsNewNetwork(newNetwork);
         result.setNetworkId(netId);
         return result;
     }
@@ -1234,7 +1283,8 @@
         if (ipChanged || proxyChanged) {
             currentConfig.linkProperties = linkProperties;
             writeIpAndProxyConfigurations();
-            sendConfiguredNetworksChangedBroadcast();
+            sendConfiguredNetworksChangedBroadcast(currentConfig,
+                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
         }
         return new NetworkUpdateResult(ipChanged, proxyChanged);
     }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d746810..8aa613b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -294,12 +294,53 @@
 
     /**
      * Broadcast intent action indicating that the configured networks changed.
-     * This can be as a result of adding/updating/deleting a network
+     * This can be as a result of adding/updating/deleting a network. If
+     * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration
+     * can be retreived with the {@link #EXTRA_WIFI_CONFIGURATION} extra. If multiple
+     * Wi-Fi configurations changed, {@link #EXTRA_WIFI_CONFIGURATION} will not be present.
      * @hide
      */
     public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =
         "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
     /**
+     * The lookup key for a (@link android.net.wifi.WifiConfiguration} object representing
+     * the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION}
+     * broadcast is sent.
+     * @hide
+     */
+    public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+    /**
+     * Multiple network configurations have changed.
+     * @see #CONFIGURED_NETWORKS_CHANGED_ACTION
+     *
+     * @hide
+     */
+    public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+    /**
+     * The lookup key for an integer indicating the reason a Wi-Fi network configuration
+     * has changed. Only present if {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is {@code false}
+     * @see #CONFIGURED_NETWORKS_CHANGED_ACTION
+     * @hide
+     */
+    public static final String EXTRA_CHANGE_REASON = "changeReason";
+    /**
+     * The configuration is new and was added.
+     * @hide
+     */
+    public static final int CHANGE_REASON_ADDED = 0;
+    /**
+     * The configuration was removed and is no longer present in the system's list of
+     * configured networks.
+     * @hide
+     */
+    public static final int CHANGE_REASON_REMOVED = 1;
+    /**
+     * The configuration has changed as a result of explicit action or because the system
+     * took an automated action such as disabling a malfunctioning configuration.
+     * @hide
+     */
+    public static final int CHANGE_REASON_CONFIG_CHANGE = 2;
+    /**
      * An access point scan has completed, and results are available from the supplicant.
      * Call {@link #getScanResults()} to obtain the results.
      */