- keep track of ephemeral configurations that are deleted by user
-  make sure we cleaned up prior ephemeral WiFiconfiguration from wpa_supplicant.conf
Bug:18525241

Change-Id: I773cb9b28fe139d1cd5df622619b4a9cf866d6ce
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index a971dcd..9a84cee 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -1489,6 +1489,11 @@
                             !isOpenNetwork(result)) {
                         continue;
                     }
+                    if (mWifiConfigStore.mDeletedEphemeralSSIDs.contains
+                            ("\"" + result.SSID + "\"")) {
+                        // SSID had been Forgotten by user, then don't score it
+                        continue;
+                    }
                     if ((nowMs - result.seen) < mScanResultAutoJoinAge) {
                         // Increment usage count for the network
                         mWifiConnectionStatistics.incrementOrAddUntrusted(result.SSID, 0, 1);
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index c7b8ab3..1aa830d 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -158,6 +158,15 @@
      */
     private Set<Long> mDeletedSSIDs = new HashSet<Long>();
 
+    /**
+     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
+     * so as, framework knows not to autojoin again those SSIDs based on scorer input.
+     * The list is never cleared up.
+     *
+     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
+     */
+    public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
+
     /* Tracks the highest priority of configured networks */
     private int mLastPriority = -1;
 
@@ -209,6 +218,7 @@
     private static final String EPHEMERAL_KEY = "EPHEMERAL:   ";
     private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION:  ";
     private static final String DELETED_CRC32_KEY = "DELETED_CRC32:  ";
+    private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL:  ";
 
     private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST:  ";
     private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
@@ -830,6 +840,15 @@
                     + " Uid=" + Integer.toString(config.creatorUid)
                     + "/" + Integer.toString(config.lastUpdateUid));
         }
+
+        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
+            if (VDBG) {
+                loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);
+            }
+            // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
+            // below, since we're creating/modifying a config.
+        }
+
         boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
         NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
         int netId = result.getNetworkId();
@@ -947,6 +966,42 @@
         }
     }
 
+
+    /**
+     * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
+     * This SSID will never be scored anymore.
+     * The only way to "un-disable it" is if the user create a network for that SSID and then
+     * forget it.
+     *
+     * @param SSID caller must ensure that the SSID passed thru this API match
+     *            the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
+     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
+     *         disconnect if this is the current network.
+     */
+    WifiConfiguration disableEphemeralNetwork(String SSID) {
+        if (SSID == null) {
+            return null;
+        }
+
+        WifiConfiguration foundConfig = null;
+
+        mDeletedEphemeralSSIDs.add(SSID);
+        loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size());
+
+        for (WifiConfiguration config : mConfiguredNetworks.values()) {
+            if (SSID.equals(config.SSID) && config.ephemeral) {
+                loge("Found ephemeral config in disableEphemeralNetwork: " + config.networkId);
+                foundConfig = config;
+            }
+        }
+
+        // Force a write, because the mDeletedEphemeralSSIDs list has changed even though the
+        // configurations may not have.
+        writeKnownNetworkHistory(true);
+
+        return foundConfig;
+    }
+
     /**
      * Forget the specified network and save config
      *
@@ -1719,6 +1774,13 @@
                         out.writeUTF(SEPARATOR_KEY);
                     }
                 }
+                if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) {
+                    for (String ssid : mDeletedEphemeralSSIDs) {
+                        out.writeUTF(DELETED_EPHEMERAL_KEY);
+                        out.writeUTF(ssid);
+                        out.writeUTF(SEPARATOR_KEY);
+                    }
+                }
             }
         });
     }
@@ -2049,6 +2111,13 @@
                             Long c = Long.parseLong(crc);
                             mDeletedSSIDs.add(c);
                         }
+                        if (key.startsWith(DELETED_EPHEMERAL_KEY)) {
+                            String s = key.replace(DELETED_EPHEMERAL_KEY, "");
+                            if (!TextUtils.isEmpty(s)) {
+                                s = s.replace(SEPARATOR_KEY, "");
+                                mDeletedEphemeralSSIDs.add(s);
+                            }
+                        }
                     }
                 }
             }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index bcaab92..11d0259 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -1302,6 +1302,14 @@
         return new Messenger(mClientHandler);
     }
 
+    /**
+     * Disable an ephemeral network, i.e. network that is created thru a WiFi Scorer
+     */
+    public void disableEphemeralNetwork(String SSID) {
+        enforceAccessPermission();
+        enforceChangePermission();
+        mWifiStateMachine.disableEphemeralNetwork(SSID);
+    }
 
     /**
      * Get the IP and proxy configuration file
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 09920f5..220ca74 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -578,6 +578,9 @@
     /* Disconnecting state watchdog */
     static final int CMD_DISCONNECTING_WATCHDOG_TIMER     = BASE + 96;
 
+    /* Disable an ephemeral network */
+    static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98;
+
     /* P2p commands */
     /* We are ok with no response here since we wont do much with it anyway */
     public static final int CMD_ENABLE_P2P                = BASE + 131;
@@ -2019,6 +2022,12 @@
         }
     }
 
+    public void disableEphemeralNetwork(String SSID) {
+        if (SSID != null) {
+            sendMessage(CMD_DISABLE_EPHEMERAL_NETWORK, SSID);
+        }
+    }
+
     /**
      * Get unsynchronized pointer to scan result list
      * Can be called only from AutoJoinController which runs in the WifiStateMachine context
@@ -4713,6 +4722,7 @@
                 case CMD_UNWANTED_NETWORK:
                 case CMD_DISCONNECTING_WATCHDOG_TIMER:
                 case CMD_ROAM_WATCHDOG_TIMER:
+                case CMD_DISABLE_EPHEMERAL_NETWORK:
                     messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
                     break;
                 case DhcpStateMachine.CMD_ON_QUIT:
@@ -5691,6 +5701,9 @@
             case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
                 s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
                 break;
+            case CMD_DISABLE_EPHEMERAL_NETWORK:
+                s = "CMD_DISABLE_EPHEMERAL_NETWORK";
+                break;
             case CMD_START_DRIVER:
                 s = "CMD_START_DRIVER";
                 break;
@@ -6025,6 +6038,10 @@
             WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
             if (config != null) {
                 config.lastDisconnected = System.currentTimeMillis();
+                if (config.ephemeral) {
+                    // Remove ephemeral WifiConfigurations from file
+                    mWifiConfigStore.forgetNetwork(mLastNetworkId);
+                }
             }
         }
     }
@@ -6240,6 +6257,15 @@
                                 WifiManager.ERROR);
                     }
                     break;
+                case CMD_DISABLE_EPHEMERAL_NETWORK:
+                    config = mWifiConfigStore.disableEphemeralNetwork((String)message.obj);
+                    if (config != null) {
+                        if (config.networkId == mLastNetworkId) {
+                            // Disconnect and let autojoin reselect a new network
+                            sendMessage(CMD_DISCONNECT);
+                        }
+                    }
+                    break;
                 case CMD_BLACKLIST_NETWORK:
                     mWifiNative.addToBlacklist((String) message.obj);
                     break;