am dcf7577b: am d32033b8: am 8b41627f: Fix a race in WifiNative objects

* commit 'dcf7577b0243059412c5508c79cbbbf6734cb555':
  Fix a race in WifiNative objects
diff --git a/service/Android.mk b/service/Android.mk
old mode 100755
new mode 100644
index 971c983..f325782
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,7 +16,7 @@
 
 ifneq ($(TARGET_BUILD_PDK), true)
 
-# Make HAL stub library
+# Make HAL stub library 1
 # ============================================================
 
 include $(CLEAR_VARS)
@@ -36,6 +36,27 @@
 
 include $(BUILD_STATIC_LIBRARY)
 
+# Make HAL stub library 2
+# ============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_REQUIRED_MODULES :=
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/jni \
+	external/libnl-headers \
+	$(call include-path-for, libhardware_legacy)/hardware_legacy
+
+LOCAL_SRC_FILES := \
+	lib/wifi_hal_stub.cpp
+
+LOCAL_MODULE := libwifi-hal-stub
+
+include $(BUILD_STATIC_LIBRARY)
+
 # set correct hal library path
 # ============================================================
 LIB_WIFI_HAL := libwifi-hal
@@ -43,9 +64,7 @@
 ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
   LIB_WIFI_HAL := libwifi-hal-bcm
 else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
-  # this is commented because none of the nexus devices
-  # that sport Qualcomm's wifi have support for HAL
-  # LIB_WIFI_HAL := libwifi-hal-qcom
+  LIB_WIFI_HAL := libwifi-hal-qcom
 else ifeq ($(BOARD_WLAN_DEVICE), mrvl)
   # this is commented because none of the nexus devices
   # that sport Marvell's wifi have support for HAL
@@ -55,35 +74,6 @@
   LIB_WIFI_HAL := libwifi-hal-mt66xx
 endif
 
-# Build the HalUtil
-# ============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_REQUIRED_MODULES := libandroid_runtime libhardware_legacy
-
-LOCAL_CFLAGS += -Wno-unused-parameter
-
-LOCAL_C_INCLUDES += \
-	$(call include-path-for, libhardware)/hardware \
-	$(call include-path-for, libhardware_legacy)/hardware_legacy \
-	libcore/include
-
-LOCAL_SHARED_LIBRARIES += \
-	libcutils \
-	libnl \
-	libandroid_runtime \
-	libutils
-
-LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
-
-LOCAL_SRC_FILES := \
-	tools/halutil/halutil.cpp
-
-LOCAL_MODULE := halutil
-
-include $(BUILD_EXECUTABLE)
-
 # Make the JNI part
 # ============================================================
 include $(CLEAR_VARS)
@@ -105,8 +95,10 @@
 	libhardware \
 	libhardware_legacy \
 	libandroid_runtime \
-    libnl
+	libnl \
+    libdl
 
+LOCAL_STATIC_LIBRARIES += libwifi-hal-stub
 LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
 
 LOCAL_SRC_FILES := \
@@ -129,7 +121,7 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libandroid_runtime
 LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt services
-LOCAL_STATIC_JAVA_LIBRARIES := ksoap2
+LOCAL_STATIC_JAVA_LIBRARIES := ksoap2 android-support-v4
 LOCAL_REQUIRED_MODULES := services
 LOCAL_MODULE_TAGS :=
 LOCAL_MODULE := wifi-service
diff --git a/service/java/com/android/server/wifi/ConfigurationMap.java b/service/java/com/android/server/wifi/ConfigurationMap.java
new file mode 100644
index 0000000..0fb56ea
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConfigurationMap.java
@@ -0,0 +1,146 @@
+package com.android.server.wifi;
+
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class ConfigurationMap {
+    private final Map<Integer, WifiConfiguration> mPerID = new HashMap<>();
+    private final Map<Integer, WifiConfiguration> mPerConfigKey = new HashMap<>();
+    private final Map<String, Integer> mPerFQDN = new HashMap<>();
+
+    // RW methods:
+    public WifiConfiguration put(int netid, WifiConfiguration config) {
+        WifiConfiguration current = mPerID.put(netid, config);
+        mPerConfigKey.put(config.configKey().hashCode(), config);   // This is ridiculous...
+        if (config.FQDN != null && config.FQDN.length() > 0) {
+            mPerFQDN.put(config.FQDN, netid);
+        }
+        return current;
+    }
+
+    public void populatePasspointData(Collection<HomeSP> homeSPs, WifiNative wifiNative) {
+        mPerFQDN.clear();
+
+        for (HomeSP homeSp : homeSPs) {
+            String fqdn = homeSp.getFQDN();
+            Log.d(WifiConfigStore.TAG, "Looking for " + fqdn);
+            for (WifiConfiguration config : mPerID.values()) {
+                Log.d(WifiConfigStore.TAG, "Testing " + config.SSID);
+
+                String id_str = Utils.unquote(wifiNative.getNetworkVariable(
+                        config.networkId, WifiConfigStore.idStringVarName));
+                if (id_str != null && id_str.equals(fqdn) && config.enterpriseConfig != null) {
+                    Log.d(WifiConfigStore.TAG, "Matched " + id_str + " with " + config.networkId);
+                    config.FQDN = fqdn;
+                    config.providerFriendlyName = homeSp.getFriendlyName();
+
+                    HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums();
+                    config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
+                    int i = 0;
+                    for (long id : roamingConsortiumIds) {
+                        config.roamingConsortiumIds[i] = id;
+                        i++;
+                    }
+                    IMSIParameter imsiParameter = homeSp.getCredential().getImsi();
+                    config.enterpriseConfig.setPlmn(
+                            imsiParameter != null ? imsiParameter.toString() : null);
+                    config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
+                    mPerFQDN.put(fqdn, config.networkId);
+                }
+            }
+        }
+
+        Log.d(WifiConfigStore.TAG, "loaded " + mPerFQDN.size() + " passpoint configs");
+    }
+
+    public WifiConfiguration remove(int netID) {
+        WifiConfiguration config = mPerID.remove(netID);
+        if (config == null) {
+            return null;
+        }
+        mPerConfigKey.remove(config.configKey().hashCode());
+
+        Iterator<Map.Entry<String, Integer>> entries = mPerFQDN.entrySet().iterator();
+        while (entries.hasNext()) {
+            if (entries.next().getValue() == netID) {
+                entries.remove();
+                break;
+            }
+        }
+        return config;
+    }
+
+    public void clear() {
+        mPerID.clear();
+        mPerConfigKey.clear();
+        mPerFQDN.clear();
+    }
+
+    // RO methods:
+    public WifiConfiguration get(int netid) {
+        return mPerID.get(netid);
+    }
+
+    public int size() {
+        return mPerID.size();
+    }
+
+    public boolean isEmpty() {
+        return mPerID.size() == 0;
+    }
+
+    public WifiConfiguration getByFQDN(String fqdn) {
+        Integer id = mPerFQDN.get(fqdn);
+        return id != null ? mPerID.get(id) : null;
+    }
+
+    public WifiConfiguration getByConfigKey(String key) {
+        if (key == null) {
+            return null;
+        }
+        for (WifiConfiguration config : mPerID.values()) {
+            if (config.configKey().equals(key)) {
+                return config;
+            }
+        }
+        return null;
+    }
+
+    public WifiConfiguration getByConfigKeyID(int id) {
+        return mPerConfigKey.get(id);
+    }
+
+    public Collection<WifiConfiguration> getEnabledNetworks() {
+        List<WifiConfiguration> list = new ArrayList<>();
+        for (WifiConfiguration config : mPerID.values()) {
+            if (config.status != WifiConfiguration.Status.DISABLED) {
+                list.add(config);
+            }
+        }
+        return list;
+    }
+
+    public WifiConfiguration getEphemeral(String ssid) {
+        for (WifiConfiguration config : mPerID.values()) {
+            if (ssid.equals(config.SSID) && config.ephemeral) {
+                return config;
+            }
+        }
+        return null;
+    }
+
+    public Collection<WifiConfiguration> values() {
+        return mPerID.values();
+    }
+}
diff --git a/service/java/com/android/server/wifi/IMSIParameter.java b/service/java/com/android/server/wifi/IMSIParameter.java
new file mode 100644
index 0000000..deea870
--- /dev/null
+++ b/service/java/com/android/server/wifi/IMSIParameter.java
@@ -0,0 +1,102 @@
+package com.android.server.wifi;
+
+import java.io.IOException;
+
+public class IMSIParameter {
+    private final String mImsi;
+    private final boolean mPrefix;
+
+    public IMSIParameter(String imsi, boolean prefix) {
+        mImsi = imsi;
+        mPrefix = prefix;
+    }
+
+    public IMSIParameter(String imsi) throws IOException {
+        if (imsi == null || imsi.length() == 0) {
+            throw new IOException("Bad IMSI: '" + imsi + "'");
+        }
+
+        int nonDigit;
+        char stopChar = '\0';
+        for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
+            stopChar = imsi.charAt(nonDigit);
+            if (stopChar < '0' || stopChar > '9') {
+                break;
+            }
+        }
+
+        if (nonDigit == imsi.length()) {
+            mImsi = imsi;
+            mPrefix = false;
+        }
+        else if (nonDigit == imsi.length()-1 && stopChar == '*') {
+            mImsi = imsi.substring(0, nonDigit);
+            mPrefix = true;
+        }
+        else {
+            throw new IOException("Bad IMSI: '" + imsi + "'");
+        }
+    }
+
+    public boolean matches(String fullIMSI) {
+        if (mPrefix) {
+            return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length());
+        }
+        else {
+            return mImsi.equals(fullIMSI);
+        }
+    }
+
+    public boolean matchesMccMnc(String mccMnc) {
+        if (mPrefix) {
+            // For a prefix match, the entire prefix must match the mcc+mnc
+            return mImsi.regionMatches(false, 0, mccMnc, 0, mImsi.length());
+        }
+        else {
+            // For regular match, the entire length of mcc+mnc must match this IMSI
+            return mImsi.regionMatches(false, 0, mccMnc, 0, mccMnc.length());
+        }
+    }
+
+    public boolean isPrefix() {
+        return mPrefix;
+    }
+
+    public String getImsi() {
+        return mImsi;
+    }
+
+    public int prefixLength() {
+        return mImsi.length();
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        else if (thatObject == null || getClass() != thatObject.getClass()) {
+            return false;
+        }
+
+        IMSIParameter that = (IMSIParameter) thatObject;
+        return mPrefix == that.mPrefix && mImsi.equals(that.mImsi);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mImsi != null ? mImsi.hashCode() : 0;
+        result = 31 * result + (mPrefix ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        if (mPrefix) {
+            return mImsi + '*';
+        }
+        else {
+            return mImsi;
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/RttService.java b/service/java/com/android/server/wifi/RttService.java
index c447e28..cc7f549 100644
--- a/service/java/com/android/server/wifi/RttService.java
+++ b/service/java/com/android/server/wifi/RttService.java
@@ -28,8 +28,9 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Queue;
+import android.Manifest;
 
-class RttService extends SystemService {
+public final class RttService extends SystemService {
 
     public static final boolean DBG = true;
 
@@ -144,6 +145,16 @@
             Integer key;
             ClientInfo ci;
             RttManager.RttParams[] params;
+
+            @Override
+            public String toString() {
+                String str = getClass().getName() + "@" + Integer.toHexString(hashCode());
+                if(this.key != null) {
+                    return str + " key: " + this.key;
+                } else {
+                    return str + " key: " + " , null";
+                }
+            }
         }
 
         private class ClientInfo {
@@ -198,12 +209,14 @@
             }
 
             void reportAborted(int key) {
-                mChannel.sendMessage(RttManager.CMD_OP_ABORTED, key);
-                mRequests.remove(key);
+                mChannel.sendMessage(RttManager.CMD_OP_ABORTED, 0, key);
+                //All Queued RTT request will be cleaned
+                cleanup();
             }
 
             void cleanup() {
                 mRequests.clear();
+                mRequestQueue.clear();
             }
         }
 
@@ -271,20 +284,32 @@
                             transitionTo(mRequestPendingState);
                             break;
                         case RttManager.CMD_OP_START_RANGING: {
-                                RttManager.ParcelableRttParams params =
-                                        (RttManager.ParcelableRttParams)msg.obj;
-                                if (params == null) {
-                                    replyFailed(msg,
-                                            RttManager.REASON_INVALID_REQUEST, "No params");
-                                } else if (ci.addRttRequest(msg.arg2, params) == false) {
-                                    replyFailed(msg,
-                                            RttManager.REASON_INVALID_REQUEST, "Unspecified");
-                                } else {
-                                    sendMessage(CMD_ISSUE_NEXT_REQUEST);
-                                }
+                            //check permission
+                            if(DBG) Log.d(TAG, "UID is: " + msg.sendingUid);
+                            if (!enforcePermissionCheck(msg)) {
+                                Log.e(TAG, "UID: " + msg.sendingUid + " has no" +
+                                        " LOCATION_HARDWARE Permission");
+                                break;
                             }
+
+                            RttManager.ParcelableRttParams params =
+                                    (RttManager.ParcelableRttParams)msg.obj;
+                            if (params == null) {
+                                replyFailed(msg,
+                                        RttManager.REASON_INVALID_REQUEST, "No params");
+                            } else if (ci.addRttRequest(msg.arg2, params) == false) {
+                                replyFailed(msg,
+                                        RttManager.REASON_INVALID_REQUEST, "Unspecified");
+                            } else {
+                                sendMessage(CMD_ISSUE_NEXT_REQUEST);
+                            }
+                        }
                             break;
                         case RttManager.CMD_OP_STOP_RANGING:
+                            if(!enforcePermissionCheck(msg)) {
+                                break;
+                            }
+
                             for (Iterator<RttRequest> it = mRequestQueue.iterator();
                                     it.hasNext(); ) {
                                 RttRequest request = it.next();
@@ -312,6 +337,7 @@
                         case CMD_DRIVER_UNLOADED:
                             if (mOutstandingRequest != null) {
                                 WifiNative.cancelRtt(mOutstandingRequest.params);
+                                if (DBG) Log.d(TAG, "abort key: " + mOutstandingRequest.key);
                                 mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
                                 mOutstandingRequest = null;
                             }
@@ -323,23 +349,38 @@
                                 if (mOutstandingRequest == null) {
                                     transitionTo(mEnabledState);
                                 }
+                                if(mOutstandingRequest != null) {
+                                    if (DBG) Log.d(TAG, "new mOutstandingRequest.key is: " +
+                                            mOutstandingRequest.key);
+                                } else {
+                                    if (DBG) Log.d(TAG,
+                                            "CMD_ISSUE_NEXT_REQUEST: mOutstandingRequest =null ");
+                                }
                             } else {
                                 /* just wait; we'll issue next request after
                                  * current one is finished */
-                                if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
+                                 if (DBG) Log.d(TAG, "Current mOutstandingRequest.key is: " +
+                                         mOutstandingRequest.key);
+                                 if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
                             }
                             break;
                         case CMD_RTT_RESPONSE:
-                            if (DBG) Log.d(TAG, "Received an RTT response");
+                            if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2);
                             mOutstandingRequest.ci.reportResult(
                                     mOutstandingRequest, (RttManager.RttResult[])msg.obj);
                             mOutstandingRequest = null;
                             sendMessage(CMD_ISSUE_NEXT_REQUEST);
                             break;
                         case RttManager.CMD_OP_STOP_RANGING:
+                            if(!enforcePermissionCheck(msg)) {
+                                Log.e(TAG, "UID: " + msg.sendingUid + " has no " +
+                                        "LOCATION_HARDWARE Permission");
+                                break;
+                            }
+
                             if (mOutstandingRequest != null
                                     && msg.arg2 == mOutstandingRequest.key) {
-                                if (DBG) Log.d(TAG, "Cancelling ongoing RTT");
+                                if (DBG) Log.d(TAG, "Cancelling ongoing RTT of: " + msg.arg2);
                                 WifiNative.cancelRtt(mOutstandingRequest.params);
                                 mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
                                 mOutstandingRequest = null;
@@ -390,6 +431,17 @@
             }
         }
 
+        boolean enforcePermissionCheck(Message msg) {
+            try {
+                mContext.enforcePermission(Manifest.permission.LOCATION_HARDWARE,
+                         -1, msg.sendingUid, "LocationRTT");
+            } catch (SecurityException e) {
+                replyFailed(msg,RttManager.REASON_PERMISSION_DENIED, "No params");
+                return false;
+            }
+            return true;
+        }
+
         private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
             @Override
             public void onRttResults(RttManager.RttResult[] result) {
@@ -401,12 +453,15 @@
             RttRequest request = null;
             while (mRequestQueue.isEmpty() == false) {
                 request = mRequestQueue.remove();
-                if (WifiNative.requestRtt(request.params, mEventHandler)) {
-                    if (DBG) Log.d(TAG, "Issued next RTT request");
-                    return request;
-                } else {
-                    request.ci.reportFailed(request,
-                            RttManager.REASON_UNSPECIFIED, "Failed to start");
+                if(request !=  null) {
+                    if (WifiNative.requestRtt(request.params, mEventHandler)) {
+                        if (DBG) Log.d(TAG, "Issued next RTT request with key: " + request.key);
+                        return request;
+                    } else {
+                        Log.e(TAG, "Fail to issue key at native layer");
+                        request.ci.reportFailed(request,
+                                RttManager.REASON_UNSPECIFIED, "Failed to start");
+                    }
                 }
             }
 
@@ -414,6 +469,10 @@
             if (DBG) Log.d(TAG, "No more requests left");
             return null;
         }
+        @Override
+        public RttManager.RttCapabilities getRttCapabilities() {
+            return WifiNative.getRttCapabilities();
+        }
     }
 
     private static final String TAG = "RttService";
@@ -442,4 +501,6 @@
             mImpl.startService(getContext());
         }
     }
+
+
 }
diff --git a/service/java/com/android/server/wifi/SIMAccessor.java b/service/java/com/android/server/wifi/SIMAccessor.java
new file mode 100644
index 0000000..e446436
--- /dev/null
+++ b/service/java/com/android/server/wifi/SIMAccessor.java
@@ -0,0 +1,32 @@
+package com.android.server.wifi;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SIMAccessor {
+    private final TelephonyManager mTelephonyManager;
+    private final SubscriptionManager mSubscriptionManager;
+
+    public SIMAccessor(Context context) {
+        mTelephonyManager = TelephonyManager.from(context);
+        mSubscriptionManager = SubscriptionManager.from(context);
+    }
+
+    public List<String> getMatchingImsis(IMSIParameter mccMnc) {
+        if (mccMnc == null) {
+            return null;
+        }
+        List<String> imsis = new ArrayList<>();
+        for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) {
+            String imsi = mTelephonyManager.getSubscriberId(subId);
+            if (mccMnc.matches(imsi)) {
+                imsis.add(imsi);
+            }
+        }
+        return imsis.isEmpty() ? null : imsis;
+    }
+}
diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java
new file mode 100644
index 0000000..868a192
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -0,0 +1,157 @@
+package com.android.server.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiSsid;
+
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.anqp.HSFriendlyNameElement;
+import com.android.server.wifi.anqp.VenueNameElement;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointMatchInfo;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ScanDetail {
+    private final ScanResult mScanResult;
+    private volatile NetworkDetail mNetworkDetail;
+    private final Map<HomeSP, PasspointMatch> mMatches;
+    private long mSeen = 0;
+
+    public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String BSSID,
+                      String caps, int level, int frequency, long tsf) {
+        mNetworkDetail = networkDetail;
+        mScanResult = new ScanResult(wifiSsid, BSSID, caps, level, frequency, tsf );
+        mSeen = System.currentTimeMillis();
+        //mScanResult.seen = mSeen;
+        mScanResult.channelWidth = networkDetail.getChannelWidth();
+        mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
+        mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
+        if (networkDetail.is80211McResponderSupport())
+            mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+        mMatches = null;
+    }
+
+    public ScanDetail(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
+                      long tsf, long seen) {
+        mNetworkDetail = null;
+        mScanResult = new ScanResult(wifiSsid, BSSID, caps, level, frequency, tsf );
+        mSeen = seen;
+        //mScanResult.seen = mSeen;
+        mScanResult.channelWidth = 0;
+        mScanResult.centerFreq0 = 0;
+        mScanResult.centerFreq1 = 0;
+        mScanResult.flags = 0;
+        mMatches = null;
+    }
+
+    private ScanDetail(ScanResult scanResult, NetworkDetail networkDetail,
+                       Map<HomeSP, PasspointMatch> matches) {
+        mScanResult = scanResult;
+        mNetworkDetail = networkDetail;
+        mMatches = matches;
+    }
+
+    public void updateResults(NetworkDetail networkDetail, int level, WifiSsid wssid, String ssid,
+                              String flags, int freq, long tsf) {
+        mScanResult.level = level;
+        mScanResult.wifiSsid = wssid;
+        // Keep existing API
+        mScanResult.SSID = ssid;
+        mScanResult.capabilities = flags;
+        mScanResult.frequency = freq;
+        mScanResult.timestamp = tsf;
+        mSeen = System.currentTimeMillis();
+        //mScanResult.seen = mSeen;
+        mScanResult.channelWidth = networkDetail.getChannelWidth();
+        mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
+        mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
+        if (networkDetail.is80211McResponderSupport())
+            mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+        if (networkDetail.isInterworking())
+            mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
+    }
+
+    public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+        if (anqpElements.isEmpty()) {
+            return;
+        }
+        mNetworkDetail = mNetworkDetail.complete(anqpElements);
+        HSFriendlyNameElement fne = (HSFriendlyNameElement)anqpElements.get(
+                Constants.ANQPElementType.HSFriendlyName);
+        // !!! Match with language
+        if (fne != null && !fne.getNames().isEmpty()) {
+            mScanResult.venueName = fne.getNames().get(0).getText();
+        } else {
+            VenueNameElement vne =
+                    (((VenueNameElement)anqpElements.get(Constants.ANQPElementType.ANQPVenueName)));
+            if (vne != null && !vne.getNames().isEmpty()) {
+                mScanResult.venueName = vne.getNames().get(0).getText();
+            }
+        }
+    }
+
+    public ScanResult getScanResult() {
+        return mScanResult;
+    }
+
+    public NetworkDetail getNetworkDetail() {
+        return mNetworkDetail;
+    }
+
+    public String getSSID() {
+        return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID();
+    }
+
+    public String getBSSIDString() {
+        return  mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString();
+    }
+
+    public String toKeyString() {
+        NetworkDetail networkDetail = mNetworkDetail;
+        if (networkDetail != null) {
+            return networkDetail.toKeyString();
+        }
+        else {
+            return String.format("'%s':%012x", mScanResult.BSSID, Utils.parseMac(mScanResult.BSSID));
+        }
+    }
+
+    public long getSeen() {
+        return mSeen;
+    }
+
+    public long setSeen() {
+        mSeen = System.currentTimeMillis();
+        mScanResult.seen = mSeen;
+        return mSeen;
+    }
+
+    public List<PasspointMatchInfo> getMatchList() {
+        if (mMatches == null || mMatches.isEmpty()) {
+            return null;
+        }
+
+        List<PasspointMatchInfo> list = new ArrayList<>();
+        for (Map.Entry<HomeSP, PasspointMatch> entry : mMatches.entrySet()) {
+            new PasspointMatchInfo(entry.getValue(), this, entry.getKey());
+        }
+        return list;
+    }
+
+    @Override
+    public String toString() {
+        try {
+            return String.format("'%s'/%012x",
+                    mScanResult.SSID, Utils.parseMac(mScanResult.BSSID));
+        }
+        catch (IllegalArgumentException iae) {
+            return String.format("'%s'/----", mScanResult.BSSID);
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java
new file mode 100644
index 0000000..d246c55
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -0,0 +1,300 @@
+
+package com.android.server.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+import android.os.SystemClock;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointMatchInfo;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+class ScanDetailCache {
+
+    private static final String TAG = "ScanDetailCache";
+
+    private WifiConfiguration mConfig;
+    private HashMap<String, ScanDetail> mMap;
+    private HashMap<String, PasspointMatchInfo> mPasspointMatches;
+    private static boolean DBG=false;
+    ScanDetailCache(WifiConfiguration config) {
+        mConfig = config;
+        mMap = new HashMap();
+        mPasspointMatches = new HashMap();
+    }
+
+    void put(ScanDetail scanDetail) {
+        put(scanDetail, null, null);
+    }
+
+    void put(ScanDetail scanDetail, PasspointMatch match, HomeSP homeSp) {
+
+        mMap.put(scanDetail.getBSSIDString(), scanDetail);
+
+        if (match != null && homeSp != null) {
+            mPasspointMatches.put(scanDetail.getBSSIDString(),
+                    new PasspointMatchInfo(match, scanDetail, homeSp));
+        }
+    }
+
+    ScanResult get(String bssid) {
+        ScanDetail scanDetail = getScanDetail(bssid);
+        return scanDetail == null ? null : scanDetail.getScanResult();
+    }
+
+    ScanDetail getScanDetail(String bssid) {
+        return mMap.get(bssid);
+    }
+
+    void remove(String bssid) {
+        mMap.remove(bssid);
+    }
+
+    int size() {
+        return mMap.size();
+    }
+
+    boolean isEmpty() {
+        return size() == 0;
+    }
+
+    ScanDetail getFirst() {
+        Iterator<ScanDetail> it = mMap.values().iterator();
+        return it.hasNext() ? it.next() : null;
+    }
+
+    Collection<String> keySet() {
+        return mMap.keySet();
+    }
+
+    Collection<ScanDetail> values() {
+        return mMap.values();
+    }
+
+    public void trim(int num) {
+        int currentSize = mMap.size();
+        if (currentSize <= num) {
+            return; // Nothing to trim
+        }
+        ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
+        if (list.size() != 0) {
+            // Sort by descending timestamp
+            Collections.sort(list, new Comparator() {
+                public int compare(Object o1, Object o2) {
+                    ScanDetail a = (ScanDetail) o1;
+                    ScanDetail b = (ScanDetail) o2;
+                    if (a.getSeen() > b.getSeen()) {
+                        return 1;
+                    }
+                    if (a.getSeen() < b.getSeen()) {
+                        return -1;
+                    }
+                    return a.getBSSIDString().compareTo(b.getBSSIDString());
+                }
+            });
+        }
+        for (int i = 0; i < currentSize - num ; i++) {
+            // Remove oldest results from scan cache
+            ScanDetail result = list.get(i);
+            mMap.remove(result.getBSSIDString());
+            mPasspointMatches.remove(result.getBSSIDString());
+        }
+    }
+
+    /* @hide */
+    private ArrayList<ScanDetail> sort() {
+        ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
+        if (list.size() != 0) {
+            Collections.sort(list, new Comparator() {
+                public int compare(Object o1, Object o2) {
+                    ScanResult a = ((ScanDetail)o1).getScanResult();
+                    ScanResult b = ((ScanDetail)o2).getScanResult();
+                    if (a.numIpConfigFailures > b.numIpConfigFailures) {
+                        return 1;
+                    }
+                    if (a.numIpConfigFailures < b.numIpConfigFailures) {
+                        return -1;
+                    }
+                    if (a.seen > b.seen) {
+                        return -1;
+                    }
+                    if (a.seen < b.seen) {
+                        return 1;
+                    }
+                    if (a.level > b.level) {
+                        return -1;
+                    }
+                    if (a.level < b.level) {
+                        return 1;
+                    }
+                    return a.BSSID.compareTo(b.BSSID);
+                }
+            });
+        }
+        return list;
+    }
+
+    public WifiConfiguration.Visibility getVisibilityByRssi(long age) {
+        WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
+
+        long now_ms = System.currentTimeMillis();
+        long now_elapsed_ms = SystemClock.elapsedRealtime();
+        for(ScanDetail scanDetail : values()) {
+            ScanResult result = scanDetail.getScanResult();
+            if (scanDetail.getSeen() == 0)
+                continue;
+
+            if (result.is5GHz()) {
+                //strictly speaking: [4915, 5825]
+                //number of known BSSID on 5GHz band
+                status.num5 = status.num5 + 1;
+            } else if (result.is24GHz()) {
+                //strictly speaking: [2412, 2482]
+                //number of known BSSID on 2.4Ghz band
+                status.num24 = status.num24 + 1;
+            }
+
+            if (result.timestamp != 0) {
+                if (DBG) {
+                    Log.e("getVisibilityByRssi", " considering " + result.SSID + " " + result.BSSID
+                        + " elapsed=" + now_elapsed_ms + " timestamp=" + result.timestamp
+                        + " age = " + age);
+                }
+                if ((now_elapsed_ms - (result.timestamp/1000)) > age) continue;
+            } else {
+                // This check the time at which we have received the scan result from supplicant
+                if ((now_ms - result.seen) > age) continue;
+            }
+
+            if (result.is5GHz()) {
+                if (result.level > status.rssi5) {
+                    status.rssi5 = result.level;
+                    status.age5 = result.seen;
+                    status.BSSID5 = result.BSSID;
+                }
+            } else if (result.is24GHz()) {
+                if (result.level > status.rssi24) {
+                    status.rssi24 = result.level;
+                    status.age24 = result.seen;
+                    status.BSSID24 = result.BSSID;
+                }
+            }
+        }
+
+        return status;
+    }
+
+    public WifiConfiguration.Visibility getVisibilityByPasspointMatch(long age) {
+
+        long now_ms = System.currentTimeMillis();
+        PasspointMatchInfo pmiBest24 = null, pmiBest5 = null;
+
+        for(PasspointMatchInfo pmi : mPasspointMatches.values()) {
+            ScanDetail scanDetail = pmi.getScanDetail();
+            if (scanDetail == null) continue;
+            ScanResult result = scanDetail.getScanResult();
+            if (result == null) continue;
+
+            if (scanDetail.getSeen() == 0)
+                continue;
+
+            if ((now_ms - result.seen) > age) continue;
+
+            if (result.is5GHz()) {
+                if (pmiBest5 == null || pmiBest5.compareTo(pmi) < 0) {
+                    pmiBest5 = pmi;
+                }
+            } else if (result.is24GHz()) {
+                if (pmiBest24 == null || pmiBest24.compareTo(pmi) < 0) {
+                    pmiBest24 = pmi;
+                }
+            }
+        }
+
+        WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
+        String logMsg = "Visiblity by passpoint match returned ";
+        if (pmiBest5 != null) {
+            ScanResult result = pmiBest5.getScanDetail().getScanResult();
+            status.rssi5 = result.level;
+            status.age5 = result.seen;
+            status.BSSID5 = result.BSSID;
+            logMsg += "5 GHz BSSID of " + result.BSSID;
+        }
+        if (pmiBest24 != null) {
+            ScanResult result = pmiBest24.getScanDetail().getScanResult();
+            status.rssi24 = result.level;
+            status.age24 = result.seen;
+            status.BSSID24 = result.BSSID;
+            logMsg += "2.4 GHz BSSID of " + result.BSSID;
+        }
+
+        Log.d(TAG, logMsg);
+
+        return status;
+    }
+
+    public WifiConfiguration.Visibility getVisibility(long age) {
+        if (mConfig.isPasspoint()) {
+            return getVisibilityByPasspointMatch(age);
+        } else {
+            return getVisibilityByRssi(age);
+        }
+    }
+
+
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append("Scan Cache:  ").append('\n');
+
+        ArrayList<ScanDetail> list = sort();
+        long now_ms = System.currentTimeMillis();
+        if (list.size() > 0) {
+            for (ScanDetail scanDetail : list) {
+                ScanResult result = scanDetail.getScanResult();
+                long milli = now_ms - scanDetail.getSeen();
+                long ageSec = 0;
+                long ageMin = 0;
+                long ageHour = 0;
+                long ageMilli = 0;
+                long ageDay = 0;
+                if (now_ms > scanDetail.getSeen() && scanDetail.getSeen() > 0) {
+                    ageMilli = milli % 1000;
+                    ageSec   = (milli / 1000) % 60;
+                    ageMin   = (milli / (60*1000)) % 60;
+                    ageHour  = (milli / (60*60*1000)) % 24;
+                    ageDay   = (milli / (24*60*60*1000));
+                }
+                sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
+                sbuf.append(",").append(String.format("%3d", result.level));
+                if (result.autoJoinStatus > 0) {
+                    sbuf.append(",st=").append(result.autoJoinStatus);
+                }
+                if (ageSec > 0 || ageMilli > 0) {
+                    sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
+                            ageHour, ageMin, ageSec, ageMilli));
+                }
+                if (result.numIpConfigFailures > 0) {
+                    sbuf.append(",ipfail=");
+                    sbuf.append(result.numIpConfigFailures);
+                }
+                sbuf.append("} ");
+            }
+            sbuf.append('\n');
+        }
+
+        return sbuf.toString();
+    }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index eb35db8..ed5ddf7 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -37,6 +37,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.UUID;
 
 /**
@@ -51,7 +52,7 @@
     private static final String AP_CONFIG_FILE = Environment.getDataDirectory() +
         "/misc/wifi/softap.conf";
 
-    private static final int AP_CONFIG_FILE_VERSION = 1;
+    private static final int AP_CONFIG_FILE_VERSION = 2;
 
     private State mDefaultState = new DefaultState();
     private State mInactiveState = new InactiveState();
@@ -59,6 +60,7 @@
 
     private WifiConfiguration mWifiApConfig = null;
     private AsyncChannel mReplyChannel = new AsyncChannel();
+    public ArrayList <Integer> allowed2GChannel = null;
 
     WifiApConfigStore(Context context, Handler target) {
         super(TAG, target.getLooper());
@@ -69,6 +71,17 @@
             addState(mActiveState, mDefaultState);
 
         setInitialState(mInactiveState);
+        String ap2GChannelListStr = (mContext.getResources().getString(
+                R.string.config_wifi_framework_sap_2G_channel_list));
+        Log.d(TAG, "2G band allowed channels are:" + ap2GChannelListStr);
+
+        if (ap2GChannelListStr != null) {
+            allowed2GChannel = new ArrayList<Integer>();
+            String channelList[] = ap2GChannelListStr.split(",");
+            for (String tmp : channelList) {
+                allowed2GChannel.add(Integer.parseInt(tmp));
+            }
+        }
     }
 
     public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) {
@@ -100,9 +113,9 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case WifiStateMachine.CMD_SET_AP_CONFIG:
-                    WifiConfiguration config = (WifiConfiguration) message.obj;
+                     WifiConfiguration config = (WifiConfiguration)message.obj;
                     if (config.SSID != null) {
-                        mWifiApConfig = (WifiConfiguration) message.obj;
+                        mWifiApConfig = config;
                         transitionTo(mActiveState);
                     } else {
                         Log.e(TAG, "Try to setup AP config without SSID: " + message);
@@ -150,17 +163,24 @@
                             AP_CONFIG_FILE)));
 
             int version = in.readInt();
-            if (version != 1) {
+            if ((version != 1) && (version != 2)) {
                 Log.e(TAG, "Bad version on hotspot configuration file, set defaults");
                 setDefaultApConfiguration();
                 return;
             }
             config.SSID = in.readUTF();
+
+            if (version >= 2) {
+                config.apBand = in.readInt();
+                config.apChannel = in.readInt();
+            }
+
             int authType = in.readInt();
             config.allowedKeyManagement.set(authType);
             if (authType != KeyMgmt.NONE) {
                 config.preSharedKey = in.readUTF();
             }
+
             mWifiApConfig = config;
         } catch (IOException ignore) {
             setDefaultApConfiguration();
@@ -185,6 +205,8 @@
 
             out.writeInt(AP_CONFIG_FILE_VERSION);
             out.writeUTF(config.SSID);
+            out.writeInt(config.apBand);
+            out.writeInt(config.apChannel);
             int authType = config.getAuthType();
             out.writeInt(authType);
             if(authType != KeyMgmt.NONE) {
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index c7da179..a1a9a82 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -20,17 +20,23 @@
 import android.net.NetworkKey;
 import android.net.NetworkScoreManager;
 import android.net.WifiKey;
-import android.net.wifi.*;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.os.SystemClock;
-import android.provider.Settings;
+import android.net.wifi.WifiConnectionStatistics;
 import android.os.Process;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.os.SystemClock;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
 import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -59,12 +65,13 @@
 
     private String mCurrentConfigurationKey = null; //used by autojoin
 
-    private HashMap<String, ScanResult> scanResultCache =
-            new HashMap<String, ScanResult>();
+    private final HashMap<String, ScanDetail> scanResultCache = new HashMap<>();
 
     private WifiConnectionStatistics mWifiConnectionStatistics;
 
-    /** Whether to allow connections to untrusted networks. */
+    /**
+     * Whether to allow connections to untrusted networks.
+     */
     private boolean mAllowUntrustedConnections = false;
 
     /* For debug purpose only: if the scored override a score */
@@ -75,7 +82,9 @@
     // Lose some temporary blacklisting after 30 minutes
     private final static long loseBlackListSoftMilli = 1000 * 60 * 30;
 
-    /** @see android.provider.Settings.Global#WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS */
+    /**
+     * @see android.provider.Settings.Global#WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS
+     */
     private static final long DEFAULT_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = 1000 * 60; // 1 minute
 
     public static final int AUTO_JOIN_IDLE = 0;
@@ -85,6 +94,8 @@
 
     public static final int HIGH_THRESHOLD_MODIFIER = 5;
 
+    public static final int MAX_RSSI_DELTA = 50;
+
     // Below are AutoJoin wide parameters indicating if we should be aggressive before joining
     // weak network. Note that we cannot join weak network that are going to be marked as unanted by
     // ConnectivityService because this will trigger link flapping.
@@ -123,7 +134,7 @@
     }
 
     void enableVerboseLogging(int verbose) {
-        if (verbose > 0 ) {
+        if (verbose > 0) {
             DBG = true;
             VDBG = true;
         } else {
@@ -134,7 +145,6 @@
 
     /**
      * Flush out scan results older than mScanResultMaximumAge
-     *
      */
     private void ageScanResultsOut(int delay) {
         if (delay <= 0) {
@@ -146,18 +156,67 @@
                     + Integer.valueOf(scanResultCache.size()) + " now " + Long.valueOf(milli));
         }
 
-        Iterator<HashMap.Entry<String,ScanResult>> iter = scanResultCache.entrySet().iterator();
+        Iterator<HashMap.Entry<String, ScanDetail>> iter = scanResultCache.entrySet().iterator();
         while (iter.hasNext()) {
-            HashMap.Entry<String,ScanResult> entry = iter.next();
-            ScanResult result = entry.getValue();
-
-            if ((result.seen + delay) < milli) {
+            HashMap.Entry<String, ScanDetail> entry = iter.next();
+            ScanDetail scanDetail = entry.getValue();
+            if ((scanDetail.getSeen() + delay) < milli) {
                 iter.remove();
             }
         }
     }
 
-    int addToScanCache(List<ScanResult> scanList) {
+
+    void averageRssiAndRemoveFromCache(ScanResult result) {
+        // Fetch the previous instance for this result
+        ScanDetail sd = scanResultCache.get(result.BSSID);
+        if (sd != null) {
+            ScanResult sr = sd.getScanResult();
+            if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0
+                    && result.level == 0
+                    && sr.level < -20) {
+                // A 'zero' RSSI reading is most likely a chip problem which returns
+                // an unknown RSSI, hence ignore it
+                result.level = sr.level;
+            }
+
+            // If there was a previous cache result for this BSSID, average the RSSI values
+            result.averageRssi(sr.level, sr.seen, mScanResultMaximumAge);
+
+            // Remove the previous Scan Result - this is not necessary
+            scanResultCache.remove(result.BSSID);
+        } else if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0 && result.level == 0) {
+            // A 'zero' RSSI reading is most likely a chip problem which returns
+            // an unknown RSSI, hence initialize it to a sane value
+            result.level = mWifiConfigStore.scanResultRssiLevelPatchUp;
+        }
+    }
+
+    void addToUnscoredNetworks(ScanResult result, List<NetworkKey> unknownScanResults) {
+        WifiKey wkey;
+        // Quoted SSIDs are the only one valid at this stage
+        try {
+            wkey = new WifiKey("\"" + result.SSID + "\"", result.BSSID);
+        } catch (IllegalArgumentException e) {
+            logDbg("AutoJoinController: received badly encoded SSID=[" + result.SSID +
+                    "] ->skipping this network");
+            wkey = null;
+        }
+        if (wkey != null) {
+            NetworkKey nkey = new NetworkKey(wkey);
+            //if we don't know this scan result then request a score from the scorer
+            unknownScanResults.add(nkey);
+        }
+        if (VDBG) {
+            String cap = "";
+            if (result.capabilities != null)
+                cap = result.capabilities;
+            logDbg(result.SSID + " " + result.BSSID + " rssi="
+                    + result.level + " cap " + cap + " tsf " + result.timestamp + " is not scored");
+        }
+    }
+
+    int addToScanCache(List<ScanDetail> scanList) {
         int numScanResultsKnown = 0; // Record number of scan results we knew about
         WifiConfiguration associatedConfig = null;
         boolean didAssociate = false;
@@ -165,56 +224,24 @@
 
         ArrayList<NetworkKey> unknownScanResults = new ArrayList<NetworkKey>();
 
-        for(ScanResult result: scanList) {
+        for (ScanDetail scanDetail : scanList) {
+            ScanResult result = scanDetail.getScanResult();
             if (result.SSID == null) continue;
 
-            // Make sure we record the last time we saw this result
-            result.seen = System.currentTimeMillis();
-
-            // Fetch the previous instance for this result
-            ScanResult sr = scanResultCache.get(result.BSSID);
-            if (sr != null) {
-                if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0
-                        && result.level == 0
-                        && sr.level < -20) {
-                    // A 'zero' RSSI reading is most likely a chip problem which returns
-                    // an unknown RSSI, hence ignore it
-                    result.level = sr.level;
-                }
-
-                // If there was a previous cache result for this BSSID, average the RSSI values
-                result.averageRssi(sr.level, sr.seen, mScanResultMaximumAge);
-
-                // Remove the previous Scan Result - this is not necessary
-                scanResultCache.remove(result.BSSID);
-            } else if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0 && result.level == 0) {
-                // A 'zero' RSSI reading is most likely a chip problem which returns
-                // an unknown RSSI, hence initialize it to a sane value
-                result.level = mWifiConfigStore.scanResultRssiLevelPatchUp;
+            if (VDBG) {
+                logDbg(" addToScanCache " + result.SSID + " " + result.BSSID
+                        + " tsf=" + result.timestamp
+                        + " now= " + now + " uptime=" + SystemClock.uptimeMillis()
+                        + " elapsed=" + SystemClock.elapsedRealtime());
             }
 
+            // Make sure we record the last time we saw this result
+            scanDetail.setSeen();
+
+            averageRssiAndRemoveFromCache(result);
+
             if (!mNetworkScoreCache.isScoredNetwork(result)) {
-                WifiKey wkey;
-                // Quoted SSIDs are the only one valid at this stage
-                try {
-                    wkey = new WifiKey("\"" + result.SSID + "\"", result.BSSID);
-                } catch (IllegalArgumentException e) {
-                    logDbg("AutoJoinController: received badly encoded SSID=[" + result.SSID +
-                            "] ->skipping this network");
-                    wkey = null;
-                }
-                if (wkey != null) {
-                    NetworkKey nkey = new NetworkKey(wkey);
-                    //if we don't know this scan result then request a score from the scorer
-                    unknownScanResults.add(nkey);
-                }
-                if (VDBG) {
-                    String cap = "";
-                    if (result.capabilities != null)
-                        cap = result.capabilities;
-                    logDbg(result.SSID + " " + result.BSSID + " rssi="
-                            + result.level + " cap " + cap + " is not scored");
-                }
+                addToUnscoredNetworks(result, unknownScanResults);
             } else {
                 if (VDBG) {
                     String cap = "";
@@ -227,29 +254,15 @@
             }
 
             // scanResultCache.put(result.BSSID, new ScanResult(result));
-            scanResultCache.put(result.BSSID, result);
+            scanResultCache.put(result.BSSID, scanDetail);
             // Add this BSSID to the scanResultCache of a Saved WifiConfiguration
-            didAssociate = mWifiConfigStore.updateSavedNetworkHistory(result);
+            didAssociate = mWifiConfigStore.updateSavedNetworkHistory(scanDetail);
 
             // If not successful, try to associate this BSSID to an existing Saved WifiConfiguration
             if (!didAssociate) {
                 // We couldn't associate the scan result to a Saved WifiConfiguration
                 // Hence it is untrusted
                 result.untrusted = true;
-                associatedConfig = mWifiConfigStore.associateWithConfiguration(result);
-                if (associatedConfig != null && associatedConfig.SSID != null) {
-                    if (VDBG) {
-                        logDbg("addToScanCache save associated config "
-                                + associatedConfig.SSID + " with " + result.SSID
-                                + " status " + associatedConfig.autoJoinStatus
-                                + " reason " + associatedConfig.disableReason
-                                + " tsp " + associatedConfig.blackListTimestamp
-                                + " was " + (now - associatedConfig.blackListTimestamp));
-                    }
-                    mWifiStateMachine.sendMessage(
-                            WifiStateMachine.CMD_AUTO_SAVE_NETWORK, associatedConfig);
-                    didAssociate = true;
-                }
             } else {
                 // If the scan result has been blacklisted fir 18 hours -> unblacklist
                 if ((now - result.blackListTimestamp) > loseBlackListHardMilli) {
@@ -258,7 +271,7 @@
             }
             if (didAssociate) {
                 numScanResultsKnown++;
-                result.isAutoJoinCandidate ++;
+                result.isAutoJoinCandidate++;
             } else {
                 result.isAutoJoinCandidate = 0;
             }
@@ -279,26 +292,26 @@
 
     void logDbg(String message, boolean stackTrace) {
         if (stackTrace) {
-            Log.e(TAG, message + " stack:"
+            Log.d(TAG, message + " stack:"
                     + Thread.currentThread().getStackTrace()[2].getMethodName() + " - "
                     + Thread.currentThread().getStackTrace()[3].getMethodName() + " - "
                     + Thread.currentThread().getStackTrace()[4].getMethodName() + " - "
                     + Thread.currentThread().getStackTrace()[5].getMethodName());
         } else {
-            Log.e(TAG, message);
+            Log.d(TAG, message);
         }
     }
 
     // Called directly from WifiStateMachine
     int newSupplicantResults(boolean doAutoJoin) {
         int numScanResultsKnown;
-        List<ScanResult> scanList = mWifiStateMachine.getScanResultsListNoCopyUnsync();
+        List<ScanDetail> scanList = mWifiStateMachine.getScanResultsListNoCopyUnsync();
         numScanResultsKnown = addToScanCache(scanList);
         ageScanResultsOut(mScanResultMaximumAge);
         if (DBG) {
             logDbg("newSupplicantResults size=" + Integer.valueOf(scanResultCache.size())
-                        + " known=" + numScanResultsKnown + " "
-                        + doAutoJoin);
+                    + " known=" + numScanResultsKnown + " "
+                    + doAutoJoin);
         }
         if (doAutoJoin) {
             attemptAutoJoin();
@@ -316,7 +329,7 @@
      * results as normal.
      */
     void newHalScanResults() {
-        List<ScanResult> scanList = null;//mWifiScanner.syncGetScanResultsList();
+        List<ScanDetail> scanList = null;//mWifiScanner.syncGetScanResultsList();
         String akm = WifiParser.parse_akm(null, null);
         logDbg(akm);
         addToScanCache(scanList);
@@ -326,7 +339,7 @@
     }
 
     /**
-     *  network link quality changed, called directly from WifiTrafficPoller,
+     * network link quality changed, called directly from WifiTrafficPoller,
      * or by listening to Link Quality intent
      */
     void linkQualitySignificantChange() {
@@ -350,7 +363,7 @@
         if (currentNetwork == null) {
             // Return any absurdly high score, if we are not connected there is no current
             // network to...
-           return 1000;
+            return 1000;
         }
 
         if (candidate.configKey(true).equals(currentNetwork.configKey(true))) {
@@ -375,7 +388,7 @@
             // above RSSI/scorer based selection of linked configuration (+/- 50)
             // by reducing order by -100
             order = order - 100;
-            if (VDBG)   {
+            if (VDBG) {
                 logDbg("     ...and prefers -100 " + currentNetwork.configKey()
                         + " over " + candidate.configKey()
                         + " because it is the last selected -> "
@@ -388,7 +401,7 @@
             // above RSSI/scorer based selection of linked configuration (+/- 50)
             // by increasing order by +100
             order = order + 100;
-            if (VDBG)   {
+            if (VDBG) {
                 logDbg("     ...and prefers +100 " + candidate.configKey()
                         + " over " + currentNetwork.configKey()
                         + " because it is the last selected -> "
@@ -408,7 +421,7 @@
      *
      * @param netId
      * @param userTriggered : if the update come from WiFiManager
-     * @param connect : if the update includes a connect
+     * @param connect       : if the update includes a connect
      */
     public void updateConfigurationHistory(int netId, boolean userTriggered, boolean connect) {
         WifiConfiguration selected = mWifiConfigStore.getWifiConfiguration(netId);
@@ -472,30 +485,17 @@
                         continue;
                     }
 
-                    // Compare RSSI values so as to evaluate the strength of the user preference
-                    int order = compareWifiConfigurationsRSSI(config, selected, null);
-
-                    if (order < -30) {
-                        // Selected configuration is worse than the visible configuration
-                        // hence register a strong choice so as autojoin cannot override this
-                        // for instance, the user has select a network
-                        // with 1 bar over a network with 3 bars...
-                        choice = 60;
-                    } else if (order < -20) {
-                        choice = 50;
-                    } else if (order < -10) {
-                        choice = 40;
-                    } else if (order < 20) {
-                        // Selected configuration is about same or has a slightly better RSSI
-                        // hence register a weaker choice, here a difference of at least +/-30 in
-                        // RSSI comparison triggered by autoJoin will override the choice
-                        choice = 30;
-                    } else {
-                        // Selected configuration is better than the visible configuration
-                        // hence we do not know if the user prefers this configuration strongly
-                        choice = 20;
+                    // If the selection was made while config was visible with reasonably good RSSI
+                    // then register the user preference, else ignore
+                    if (config.visibility == null ||
+                            (config.visibility.rssi24 < mWifiConfigStore.thresholdBadRssi24.get()
+                            && config.visibility.rssi5 < mWifiConfigStore.thresholdBadRssi5.get())
+                    ) {
+                        continue;
                     }
 
+                    choice = MAX_RSSI_DELTA + 10; // Make sure the choice overrides the RSSI diff
+
                     // The selected configuration was preferred over a recently seen config
                     // hence remember the user's choice:
                     // add the recently seen config to the selected's connectChoices array
@@ -508,11 +508,6 @@
                             + " over " + config.configKey(true)
                             + " choice " + Integer.toString(choice));
 
-                    Integer currentChoice = selected.connectChoices.get(config.configKey(true));
-                    if (currentChoice != null) {
-                        // User has made this choice multiple time in a row, so bump up a lot
-                        choice += currentChoice.intValue();
-                    }
                     // Add the visible config to the selected's connect choice list
                     selected.connectChoices.put(config.configKey(true), choice);
 
@@ -525,21 +520,21 @@
                         config.connectChoices.remove(selected.configKey(true));
 
                         if (selected.linkedConfigurations != null) {
-                           // Remove the selected's linked configuration from the
-                           // recently seen config's connectChoice list
-                           for (String key : selected.linkedConfigurations.keySet()) {
-                               config.connectChoices.remove(key);
-                           }
+                            // Remove the selected's linked configuration from the
+                            // recently seen config's connectChoice list
+                            for (String key : selected.linkedConfigurations.keySet()) {
+                                config.connectChoices.remove(key);
+                            }
                         }
                     }
                 }
                 if (found == false) {
-                     // We haven't found the configuration that the user just selected in our
-                     // scan cache.
-                     // In that case we will need a new scan before attempting to connect to this
-                     // configuration anyhow and thus we can process the scan results then.
-                     logDbg("updateConfigurationHistory try to connect to an old network!! : "
-                             + selected.configKey());
+                    // We haven't found the configuration that the user just selected in our
+                    // scan cache.
+                    // In that case we will need a new scan before attempting to connect to this
+                    // configuration anyhow and thus we can process the scan results then.
+                    logDbg("updateConfigurationHistory try to connect to an old network!! : "
+                            + selected.configKey());
                 }
 
                 if (selected.connectChoices != null) {
@@ -556,39 +551,44 @@
         }
     }
 
-    int getConnectChoice(WifiConfiguration source, WifiConfiguration target) {
-        Integer choice = null;
+    int getConnectChoice(WifiConfiguration source, WifiConfiguration target, boolean strict) {
+        int choice = 0;
         if (source == null || target == null) {
             return 0;
         }
 
         if (source.connectChoices != null
                 && source.connectChoices.containsKey(target.configKey(true))) {
-            choice = source.connectChoices.get(target.configKey(true));
+            Integer val = source.connectChoices.get(target.configKey(true));
+            if (val != null) {
+                choice = val;
+            }
         } else if (source.linkedConfigurations != null) {
             for (String key : source.linkedConfigurations.keySet()) {
                 WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(key);
                 if (config != null) {
                     if (config.connectChoices != null) {
-                        choice = config.connectChoices.get(target.configKey(true));
+                        Integer val = config.connectChoices.get(target.configKey(true));
+                        if (val != null) {
+                            choice = val;
+                        }
                     }
                 }
             }
         }
 
-        if (choice == null) {
-            //We didn't find the connect choice
-            return 0;
-        } else {
-            if (choice.intValue() < 0) {
-                choice = 20; // Compatibility with older files
-            }
-            return choice.intValue();
+        if (!strict && choice == 0) {
+            // We didn't find the connect choice; fallback to some default choices
+            int sourceScore = getSecurityScore(source);
+            int targetScore = getSecurityScore(target);
+            choice = sourceScore - targetScore;
         }
+
+        return choice;
     }
 
-    int compareWifiConfigurationsFromVisibility(WifiConfiguration a, int aRssiBoost,
-             WifiConfiguration b, int bRssiBoost) {
+    int compareWifiConfigurationsFromVisibility(WifiConfiguration.Visibility a, int aRssiBoost,
+                                                String dbgA, WifiConfiguration.Visibility b, int bRssiBoost, String dbgB) {
 
         int aRssiBoost5 = 0; // 5GHz RSSI boost to apply for purpose band selection (5GHz pref)
         int bRssiBoost5 = 0; // 5GHz RSSI boost to apply for purpose band selection (5GHz pref)
@@ -606,17 +606,17 @@
          * This implements band preference where we prefer 5GHz if RSSI5 is good enough, whereas
          * we prefer 2.4GHz otherwise.
          */
-        aRssiBoost5 = rssiBoostFrom5GHzRssi(a.visibility.rssi5, a.configKey() + "->");
-        bRssiBoost5 = rssiBoostFrom5GHzRssi(b.visibility.rssi5, b.configKey() + "->");
+        aRssiBoost5 = rssiBoostFrom5GHzRssi(a.rssi5, dbgA + "->");
+        bRssiBoost5 = rssiBoostFrom5GHzRssi(b.rssi5, dbgB + "->");
 
         // Select which band to use for a
-        if (a.visibility.rssi5 + aRssiBoost5 > a.visibility.rssi24) {
+        if (a.rssi5 + aRssiBoost5 > a.rssi24) {
             // Prefer a's 5GHz
             aPrefers5GHz = true;
         }
 
         // Select which band to use for b
-        if (b.visibility.rssi5 + bRssiBoost5 > b.visibility.rssi24) {
+        if (b.rssi5 + bRssiBoost5 > b.rssi24) {
             // Prefer b's 5GHz
             bPrefers5GHz = true;
         }
@@ -626,13 +626,13 @@
                 // If both a and b are on 5GHz then we don't apply the 5GHz RSSI boost to either
                 // one, but directly compare the RSSI values, this improves stability,
                 // since the 5GHz RSSI boost can introduce large fluctuations
-                aScore = a.visibility.rssi5 + aRssiBoost;
+                aScore = a.rssi5 + aRssiBoost;
             } else {
                 // If only a is on 5GHz, then apply the 5GHz preference boost to a
-                aScore = a.visibility.rssi5 + aRssiBoost + aRssiBoost5;
+                aScore = a.rssi5 + aRssiBoost + aRssiBoost5;
             }
         } else {
-            aScore = a.visibility.rssi24 + aRssiBoost;
+            aScore = a.rssi24 + aRssiBoost;
         }
 
         if (bPrefers5GHz) {
@@ -640,29 +640,30 @@
                 // If both a and b are on 5GHz then we don't apply the 5GHz RSSI boost to either
                 // one, but directly compare the RSSI values, this improves stability,
                 // since the 5GHz RSSI boost can introduce large fluctuations
-                bScore = b.visibility.rssi5 + bRssiBoost;
+                bScore = b.rssi5 + bRssiBoost;
             } else {
                 // If only b is on 5GHz, then apply the 5GHz preference boost to b
-                bScore = b.visibility.rssi5 + bRssiBoost + bRssiBoost5;
+                bScore = b.rssi5 + bRssiBoost + bRssiBoost5;
             }
         } else {
-            bScore = b.visibility.rssi24 + bRssiBoost;
+            bScore = b.rssi24 + bRssiBoost;
         }
+
         if (VDBG) {
-            logDbg("        " + a.configKey() + " is5=" + aPrefers5GHz + " score=" + aScore
-                    + " " + b.configKey() + " is5=" + bPrefers5GHz + " score=" + bScore);
+            logDbg("        " + dbgA + " is5=" + aPrefers5GHz + " score=" + aScore
+                    + " " + dbgB + " is5=" + bPrefers5GHz + " score=" + bScore);
         }
 
         // Debug only, record RSSI comparison parameters
-        if (a.visibility != null) {
-            a.visibility.score = aScore;
-            a.visibility.currentNetworkBoost = aRssiBoost;
-            a.visibility.bandPreferenceBoost = aRssiBoost5;
+        if (a != null) {
+            a.score = aScore;
+            a.currentNetworkBoost = aRssiBoost;
+            a.bandPreferenceBoost = aRssiBoost5;
         }
-        if (b.visibility != null) {
-            b.visibility.score = bScore;
-            b.visibility.currentNetworkBoost = bRssiBoost;
-            b.visibility.bandPreferenceBoost = bRssiBoost5;
+        if (b != null) {
+            b.score = bScore;
+            b.currentNetworkBoost = bRssiBoost;
+            b.bandPreferenceBoost = bRssiBoost5;
         }
 
         // Compare a and b
@@ -686,9 +687,6 @@
         int aRssiBoost = 0;
         int bRssiBoost = 0;
 
-        int scoreA;
-        int scoreB;
-
         // Retrieve the visibility
         WifiConfiguration.Visibility astatus = a.visibility;
         WifiConfiguration.Visibility bstatus = b.visibility;
@@ -707,23 +705,25 @@
             }
         }
 
-        if (VDBG)  {
+        if (VDBG) {
             logDbg("    compareWifiConfigurationsRSSI: " + a.configKey()
-                    + " rssi=" + Integer.toString(astatus.rssi24)
-                    + "," + Integer.toString(astatus.rssi5)
-                    + " boost=" + Integer.toString(aRssiBoost)
-                    + " " + b.configKey() + " rssi="
-                    + Integer.toString(bstatus.rssi24) + ","
-                    + Integer.toString(bstatus.rssi5)
-                    + " boost=" + Integer.toString(bRssiBoost)
+                            + " rssi=" + Integer.toString(astatus.rssi24)
+                            + "," + Integer.toString(astatus.rssi5)
+                            + " boost=" + Integer.toString(aRssiBoost)
+                            + " " + b.configKey() + " rssi="
+                            + Integer.toString(bstatus.rssi24) + ","
+                            + Integer.toString(bstatus.rssi5)
+                            + " boost=" + Integer.toString(bRssiBoost)
             );
         }
 
-        order = compareWifiConfigurationsFromVisibility(a, aRssiBoost, b, bRssiBoost);
+        order = compareWifiConfigurationsFromVisibility(
+                a.visibility, aRssiBoost, a.configKey(),
+                b.visibility, bRssiBoost, b.configKey());
 
-        // Normalize the order to [-50, +50]
-        if (order > 50) order = 50;
-        else if (order < -50) order = -50;
+        // Normalize the order to [-50, +50] = [ -MAX_RSSI_DELTA, MAX_RSSI_DELTA]
+        if (order > MAX_RSSI_DELTA) order = MAX_RSSI_DELTA;
+        else if (order < -MAX_RSSI_DELTA) order = -MAX_RSSI_DELTA;
 
         if (VDBG) {
             String prefer = " = ";
@@ -750,64 +750,85 @@
 
     /**
      * b/18490330 only use scorer for untrusted networks
-     *
-    int compareWifiConfigurationsWithScorer(WifiConfiguration a, WifiConfiguration b) {
-
-        boolean aIsActive = false;
-        boolean bIsActive = false;
-
-        // Apply Hysteresis : boost RSSI of current configuration before
-        // looking up the score
-        if (null != mCurrentConfigurationKey) {
-            if (a.configKey().equals(mCurrentConfigurationKey)) {
-                aIsActive = true;
-            } else if (b.configKey().equals(mCurrentConfigurationKey)) {
-                bIsActive = true;
-            }
-        }
-        int scoreA = getConfigNetworkScore(a, mScanResultAutoJoinAge, aIsActive);
-        int scoreB = getConfigNetworkScore(b, mScanResultAutoJoinAge, bIsActive);
-
-        // Both configurations need to have a score for the scorer to be used
-        // ...and the scores need to be different:-)
-        if (scoreA == WifiNetworkScoreCache.INVALID_NETWORK_SCORE
-                || scoreB == WifiNetworkScoreCache.INVALID_NETWORK_SCORE) {
-            if (VDBG)  {
-                logDbg("    compareWifiConfigurationsWithScorer no-scores: "
-                        + a.configKey()
-                        + " "
-                        + b.configKey());
-            }
-            return 0;
-        }
-
-        if (VDBG) {
-            String prefer = " = ";
-            if (scoreA < scoreB) {
-                prefer = " < ";
-            } if (scoreA > scoreB) {
-                prefer = " > ";
-            }
-            logDbg("    compareWifiConfigurationsWithScorer " + a.configKey()
-                    + " rssi=(" + a.visibility.rssi24
-                    + "," + a.visibility.rssi5
-                    + ") num=(" + a.visibility.num24
-                    + "," + a.visibility.num5 + ")"
-                    + " sc=" + scoreA
-                    + prefer + b.configKey()
-                    + " rssi=(" + b.visibility.rssi24
-                    + "," + b.visibility.rssi5
-                    + ") num=(" + b.visibility.num24
-                    + "," + b.visibility.num5 + ")"
-                    + " sc=" + scoreB
-                    + " -> " + Integer.toString(scoreB - scoreA));
-        }
-
-        // If scoreA > scoreB, the comparison is descending hence the return value is negative
-        return scoreB - scoreA;
-    }
+     * <p/>
+     * int compareWifiConfigurationsWithScorer(WifiConfiguration a, WifiConfiguration b) {
+     * <p/>
+     * boolean aIsActive = false;
+     * boolean bIsActive = false;
+     * <p/>
+     * // Apply Hysteresis : boost RSSI of current configuration before
+     * // looking up the score
+     * if (null != mCurrentConfigurationKey) {
+     * if (a.configKey().equals(mCurrentConfigurationKey)) {
+     * aIsActive = true;
+     * } else if (b.configKey().equals(mCurrentConfigurationKey)) {
+     * bIsActive = true;
+     * }
+     * }
+     * int scoreA = getConfigNetworkScore(a, mScanResultAutoJoinAge, aIsActive);
+     * int scoreB = getConfigNetworkScore(b, mScanResultAutoJoinAge, bIsActive);
+     * <p/>
+     * // Both configurations need to have a score for the scorer to be used
+     * // ...and the scores need to be different:-)
+     * if (scoreA == WifiNetworkScoreCache.INVALID_NETWORK_SCORE
+     * || scoreB == WifiNetworkScoreCache.INVALID_NETWORK_SCORE) {
+     * if (VDBG)  {
+     * logDbg("    compareWifiConfigurationsWithScorer no-scores: "
+     * + a.configKey()
+     * + " "
+     * + b.configKey());
+     * }
+     * return 0;
+     * }
+     * <p/>
+     * if (VDBG) {
+     * String prefer = " = ";
+     * if (scoreA < scoreB) {
+     * prefer = " < ";
+     * } if (scoreA > scoreB) {
+     * prefer = " > ";
+     * }
+     * logDbg("    compareWifiConfigurationsWithScorer " + a.configKey()
+     * + " rssi=(" + a.visibility.rssi24
+     * + "," + a.visibility.rssi5
+     * + ") num=(" + a.visibility.num24
+     * + "," + a.visibility.num5 + ")"
+     * + " sc=" + scoreA
+     * + prefer + b.configKey()
+     * + " rssi=(" + b.visibility.rssi24
+     * + "," + b.visibility.rssi5
+     * + ") num=(" + b.visibility.num24
+     * + "," + b.visibility.num5 + ")"
+     * + " sc=" + scoreB
+     * + " -> " + Integer.toString(scoreB - scoreA));
+     * }
+     * <p/>
+     * // If scoreA > scoreB, the comparison is descending hence the return value is negative
+     * return scoreB - scoreA;
+     * }
      */
 
+    int getSecurityScore(WifiConfiguration config) {
+
+        if (TextUtils.isEmpty(config.SSID) == false) {
+            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
+                    || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)
+                    || config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
+                /* enterprise or PSK networks get highest score */
+                return 100;
+            } else if (config.allowedKeyManagement.get(KeyMgmt.NONE)) {
+                /* open networks have lowest score */
+                return 33;
+            }
+        } else if (TextUtils.isEmpty(config.FQDN) == false) {
+            /* passpoint networks have medium preference */
+            return 66;
+        }
+
+        /* bad network */
+        return 0;
+    }
+
     int compareWifiConfigurations(WifiConfiguration a, WifiConfiguration b) {
         int order = 0;
         boolean linked = false;
@@ -845,7 +866,7 @@
         if (!linked) {
             int choice;
 
-            choice = getConnectChoice(a, b);
+            choice = getConnectChoice(a, b, false);
             if (choice > 0) {
                 // a is of higher priority - descending
                 order = order - choice;
@@ -861,7 +882,7 @@
                 }
             }
 
-            choice = getConnectChoice(b, a);
+            choice = getConnectChoice(b, a, false);
             if (choice > 0) {
                 // a is of lower priority - ascending
                 order = order + choice;
@@ -948,13 +969,13 @@
             return 0;
         }
         if (rssi
-                > mWifiConfigStore.bandPreferenceBoostThreshold5) {
+                > mWifiConfigStore.bandPreferenceBoostThreshold5.get()) {
             // Boost by 2 dB for each point
             //    Start boosting at -65
             //    Boost by 20 if above -55
             //    Boost by 40 if abore -45
             int boost = mWifiConfigStore.bandPreferenceBoostFactor5
-                    *(rssi - mWifiConfigStore.bandPreferenceBoostThreshold5);
+                    * (rssi - mWifiConfigStore.bandPreferenceBoostThreshold5.get());
             if (boost > 50) {
                 // 50 dB boost allows jumping from 2.4 to 5GHz
                 // consistently
@@ -967,38 +988,43 @@
         }
 
         if (rssi
-                < mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+                < mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
             // penalize if < -75
             int boost = mWifiConfigStore.bandPreferencePenaltyFactor5
-                    *(rssi - mWifiConfigStore.bandPreferencePenaltyThreshold5);
+                    * (rssi - mWifiConfigStore.bandPreferencePenaltyThreshold5.get());
             return boost;
         }
         return 0;
     }
-        /**
-         * attemptRoam() function implements the core of the same SSID switching algorithm
-         *
-         * Run thru all recent scan result of a WifiConfiguration and select the
-         * best one.
-         */
+
+    /**
+     * attemptRoam() function implements the core of the same SSID switching algorithm
+     * <p/>
+     * Run thru all recent scan result of a WifiConfiguration and select the
+     * best one.
+     */
     public ScanResult attemptRoam(ScanResult a,
                                   WifiConfiguration current, int age, String currentBSSID) {
         if (current == null) {
-            if (VDBG)   {
+            if (VDBG) {
                 logDbg("attemptRoam not associated");
             }
             return a;
         }
-        if (current.scanResultCache == null) {
-            if (VDBG)   {
+
+        ScanDetailCache scanDetailCache =
+                mWifiConfigStore.getScanDetailCache(current);
+
+        if (scanDetailCache == null) {
+            if (VDBG) {
                 logDbg("attemptRoam no scan cache");
             }
             return a;
         }
-        if (current.scanResultCache.size() > 6) {
-            if (VDBG)   {
+        if (scanDetailCache.size() > 6) {
+            if (VDBG) {
                 logDbg("attemptRoam scan cache size "
-                        + current.scanResultCache.size() + " --> bail");
+                        + scanDetailCache.size() + " --> bail");
             }
             // Implement same SSID roaming only for configurations
             // that have less than 4 BSSIDs
@@ -1006,7 +1032,7 @@
         }
 
         if (current.BSSID != null && !current.BSSID.equals("any")) {
-            if (DBG)   {
+            if (DBG) {
                 logDbg("attemptRoam() BSSID is set "
                         + current.BSSID + " -> bail");
             }
@@ -1017,13 +1043,14 @@
         // relative strength of 5 and 2.4 GHz BSSIDs
         long nowMs = System.currentTimeMillis();
 
-        for (ScanResult b : current.scanResultCache.values()) {
+        for (ScanDetail sd : scanDetailCache.values()) {
+            ScanResult b = sd.getScanResult();
             int bRssiBoost5 = 0;
             int aRssiBoost5 = 0;
             int bRssiBoost = 0;
             int aRssiBoost = 0;
-            if ((b.seen == 0) || (b.BSSID == null)
-                    || ((nowMs - b.seen) > age)
+            if ((sd.getSeen() == 0) || (b.BSSID == null)
+                    || ((nowMs - sd.getSeen()) > age)
                     || b.autoJoinStatus != ScanResult.ENABLED
                     || b.numIpConfigFailures > 8) {
                 continue;
@@ -1038,10 +1065,10 @@
             if (b.numIpConfigFailures < (a.numIpConfigFailures - 1)) {
                 // Prefer a BSSID that doesn't have less number of Ip config failures
                 logDbg("attemptRoam: "
-                        + b.BSSID + " rssi=" + b.level + " ipfail=" +b.numIpConfigFailures
+                        + b.BSSID + " rssi=" + b.level + " ipfail=" + b.numIpConfigFailures
                         + " freq=" + b.frequency
                         + " > "
-                        + a.BSSID + " rssi=" + a.level + " ipfail=" +a.numIpConfigFailures
+                        + a.BSSID + " rssi=" + a.level + " ipfail=" + a.numIpConfigFailures
                         + " freq=" + a.frequency);
                 a = b;
                 continue;
@@ -1050,14 +1077,14 @@
             // Apply hysteresis: we favor the currentBSSID by giving it a boost
             if (currentBSSID != null && currentBSSID.equals(b.BSSID)) {
                 // Reduce the benefit of hysteresis if RSSI <= -75
-                if (b.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+                if (b.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
                     bRssiBoost = mWifiConfigStore.associatedHysteresisLow;
                 } else {
                     bRssiBoost = mWifiConfigStore.associatedHysteresisHigh;
                 }
             }
             if (currentBSSID != null && currentBSSID.equals(a.BSSID)) {
-                if (a.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+                if (a.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
                     // Reduce the benefit of hysteresis if RSSI <= -75
                     aRssiBoost = mWifiConfigStore.associatedHysteresisLow;
                 } else {
@@ -1082,9 +1109,9 @@
                 aRssiBoost5 = rssiBoostFrom5GHzRssi(a.level + aRssiBoost, a.BSSID);
             }
 
-            if (VDBG)  {
+            if (VDBG) {
                 String comp = " < ";
-                if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
+                if (b.level + bRssiBoost + bRssiBoost5 > a.level + aRssiBoost + aRssiBoost5) {
                     comp = " > ";
                 }
                 logDbg("attemptRoam: "
@@ -1097,13 +1124,13 @@
 
             // Compare the RSSIs after applying the hysteresis boost and the 5GHz
             // boost if applicable
-            if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
+            if (b.level + bRssiBoost + bRssiBoost5 > a.level + aRssiBoost + aRssiBoost5) {
                 // b is the better BSSID
                 a = b;
             }
         }
         if (a != null) {
-            if (VDBG)  {
+            if (VDBG) {
                 StringBuilder sb = new StringBuilder();
                 sb.append("attemptRoam: " + current.configKey() +
                         " Found " + a.BSSID + " rssi=" + a.level + " freq=" + a.frequency);
@@ -1119,9 +1146,9 @@
 
     /**
      * getNetworkScore()
-     *
+     * <p/>
      * if scorer is present, get the network score of a WifiConfiguration
-     *
+     * <p/>
      * Note: this should be merge with setVisibility
      *
      * @param config
@@ -1136,7 +1163,8 @@
             }
             return WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
         }
-        if (config.scanResultCache == null) {
+
+        if (mWifiConfigStore.getScanDetailCache(config) == null) {
             if (VDBG) {
                 logDbg("       getConfigNetworkScore for " + config.configKey()
                         + " -> no scan cache");
@@ -1150,8 +1178,9 @@
         int startScore = -10000;
 
         // Run thru all cached scan results
-        for (ScanResult result : config.scanResultCache.values()) {
-            if ((nowMs - result.seen) < age) {
+        for (ScanDetail sd : mWifiConfigStore.getScanDetailCache(config).values()) {
+            ScanResult result = sd.getScanResult();
+            if ((nowMs - sd.getSeen()) < age) {
                 int sc = mNetworkScoreCache.getNetworkScore(result, isActive);
                 if (sc > startScore) {
                     startScore = sc;
@@ -1204,8 +1233,9 @@
         // Otherwise, we stop here if the currently selected network has a score. If it doesn't, we
         // keep going - it could be that another BSSID is in range (has been seen recently) which
         // has a score, even if the one we're immediately connected to doesn't.
-        ScanResult currentScanResult =  mWifiStateMachine.getCurrentScanResult();
-        boolean currentNetworkHasScoreCurve = mNetworkScoreCache.hasScoreCurve(currentScanResult);
+        ScanResult currentScanResult = mWifiStateMachine.getCurrentScanResult();
+        boolean currentNetworkHasScoreCurve = currentScanResult != null
+                && mNetworkScoreCache.hasScoreCurve(currentScanResult);
         if (ephemeralOutOfRangeTimeoutMs <= 0 || currentNetworkHasScoreCurve) {
             if (DBG) {
                 if (currentNetworkHasScoreCurve) {
@@ -1218,14 +1248,16 @@
             return currentNetworkHasScoreCurve;
         }
 
-        if (config.scanResultCache == null || config.scanResultCache.isEmpty()) {
+        if (mWifiConfigStore.getScanDetailCache(config) == null
+                || mWifiConfigStore.getScanDetailCache(config).isEmpty()) {
             return false;
         }
 
         long currentTimeMs = System.currentTimeMillis();
-        for (ScanResult result : config.scanResultCache.values()) {
-            if (currentTimeMs > result.seen
-                    && currentTimeMs - result.seen < ephemeralOutOfRangeTimeoutMs
+        for (ScanDetail sd : mWifiConfigStore.getScanDetailCache(config).values()) {
+            ScanResult result = sd.getScanResult();
+            if (currentTimeMs > sd.getSeen()
+                    && currentTimeMs - sd.getSeen() < ephemeralOutOfRangeTimeoutMs
                     && mNetworkScoreCache.hasScoreCurve(result)) {
                 if (DBG) {
                     logDbg("Found scored BSSID, keeping network: " + result.BSSID);
@@ -1240,6 +1272,163 @@
         return false;
     }
 
+    // After WifiStateMachine ask the supplicant to associate or reconnect
+    // we might still obtain scan results from supplicant
+    // however the supplicant state in the mWifiInfo and supplicant state tracker
+    // are updated when we get the supplicant state change message which can be
+    // processed after the SCAN_RESULT message, so at this point the framework doesn't
+    // know that supplicant is ASSOCIATING.
+    // A good fix for this race condition would be for the WifiStateMachine to add
+    // a new transient state where it expects to get the supplicant message indicating
+    // that it started the association process and within which critical operations
+    // like autojoin should be deleted.
+
+    // This transient state would remove the need for the roam Wathchdog which
+    // basically does that.
+
+    // At the moment, we just query the supplicant state synchronously with the
+    // mWifiNative.status() command, which allow us to know that
+    // supplicant has started association process, even though we didnt yet get the
+    // SUPPLICANT_STATE_CHANGE message.
+
+    private static final List<String> ASSOC_STATES = Arrays.asList(
+            "ASSOCIATING",
+            "ASSOCIATED",
+            "FOUR_WAY_HANDSHAKE",
+            "GROUP_KEY_HANDSHAKE");
+
+    private int getNetID(String wpaStatus) {
+        if (VDBG) {
+            logDbg("attemptAutoJoin() status=" + wpaStatus);
+        }
+
+        try {
+            int id = WifiConfiguration.INVALID_NETWORK_ID;
+            String state = null;
+            BufferedReader br = new BufferedReader(new StringReader(wpaStatus));
+            String line;
+            while ((line = br.readLine()) != null) {
+                int split = line.indexOf('=');
+                if (split < 0) {
+                    continue;
+                }
+
+                String name = line.substring(0, split);
+                if (name.equals("id")) {
+                    try {
+                        id = Integer.parseInt(line.substring(split + 1));
+                        if (state != null) {
+                            break;
+                        }
+                    } catch (NumberFormatException nfe) {
+                        return WifiConfiguration.INVALID_NETWORK_ID;
+                    }
+                } else if (name.equals("wpa_state")) {
+                    state = line.substring(split + 1);
+                    if (ASSOC_STATES.contains(state)) {
+                        return WifiConfiguration.INVALID_NETWORK_ID;
+                    } else if (id >= 0) {
+                        break;
+                    }
+                }
+            }
+            return id;
+        } catch (IOException ioe) {
+            return WifiConfiguration.INVALID_NETWORK_ID;    // Won't happen
+        }
+    }
+
+    private boolean setCurrentConfigurationKey(WifiConfiguration currentConfig,
+                                               int supplicantNetId) {
+        if (currentConfig != null) {
+            if (supplicantNetId != currentConfig.networkId
+                    // https://b.corp.google.com/issue?id=16484607
+                    // mark this condition as an error only if the mismatched networkId are valid
+                    && supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
+                    && currentConfig.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+                logDbg("attemptAutoJoin() ERROR wpa_supplicant out of sync nid="
+                        + Integer.toString(supplicantNetId) + " WifiStateMachine="
+                        + Integer.toString(currentConfig.networkId));
+                mWifiStateMachine.disconnectCommand();
+                return false;
+            } else if (currentConfig.ephemeral && (!mAllowUntrustedConnections ||
+                    !haveRecentlySeenScoredBssid(currentConfig))) {
+                // The current connection is untrusted (the framework added it), but we're either
+                // no longer allowed to connect to such networks, the score has been nullified
+                // since we connected, or the scored BSSID has gone out of range.
+                // Drop the current connection and perform the rest of autojoin.
+                logDbg("attemptAutoJoin() disconnecting from unwanted ephemeral network");
+                mWifiStateMachine.disconnectCommand(Process.WIFI_UID,
+                        mAllowUntrustedConnections ? 1 : 0);
+                return false;
+            } else {
+                mCurrentConfigurationKey = currentConfig.configKey();
+                return true;
+            }
+        } else {
+            // If not invalid, then maybe in the process of associating, skip this attempt
+            return supplicantNetId == WifiConfiguration.INVALID_NETWORK_ID;
+        }
+    }
+
+    private void updateBlackListStatus(WifiConfiguration config, long now) {
+        // Wait for 5 minutes before reenabling config that have known,
+        // repeated connection or DHCP failures
+        if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
+                || config.disableReason
+                == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
+                || config.disableReason
+                == WifiConfiguration.DISABLED_AUTH_FAILURE) {
+            if (config.blackListTimestamp == 0
+                    || (config.blackListTimestamp > now)) {
+                // Sanitize the timestamp
+                config.blackListTimestamp = now;
+            }
+            if ((now - config.blackListTimestamp) >
+                    mWifiConfigStore.wifiConfigBlacklistMinTimeMilli) {
+                // Re-enable the WifiConfiguration
+                config.status = WifiConfiguration.Status.ENABLED;
+
+                // Reset the blacklist condition
+                config.numConnectionFailures = 0;
+                config.numIpConfigFailures = 0;
+                config.numAuthFailures = 0;
+                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
+
+                config.dirty = true;
+            } else {
+                if (VDBG) {
+                    long delay = mWifiConfigStore.wifiConfigBlacklistMinTimeMilli
+                            - (now - config.blackListTimestamp);
+                    logDbg("attemptautoJoin " + config.configKey()
+                            + " dont unblacklist yet, waiting for "
+                            + delay + " ms");
+                }
+            }
+        }
+        // Avoid networks disabled because of AUTH failure altogether
+        if (DBG) {
+            logDbg("attemptAutoJoin skip candidate due to auto join status "
+                    + Integer.toString(config.autoJoinStatus) + " key "
+                    + config.configKey(true)
+                    + " reason " + config.disableReason);
+        }
+    }
+
+    boolean underSoftThreshold(WifiConfiguration config) {
+        return config.visibility.rssi24 < mWifiConfigStore.thresholdUnblacklistThreshold24Soft.get()
+                && config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Soft.get();
+    }
+
+    boolean underHardThreshold(WifiConfiguration config) {
+        return config.visibility.rssi24 < mWifiConfigStore.thresholdUnblacklistThreshold24Hard.get()
+                && config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Hard.get();
+    }
+
+    boolean underThreshold(WifiConfiguration config, int rssi24, int rssi5) {
+        return config.visibility.rssi24 < rssi24 && config.visibility.rssi5 < rssi5;
+    }
+
     /**
      * attemptAutoJoin() function implements the core of the a network switching algorithm
      * Return false if no acceptable networks were found.
@@ -1249,79 +1438,32 @@
         didOverride = false;
         didBailDueToWeakRssi = false;
         int networkSwitchType = AUTO_JOIN_IDLE;
+        int age = mScanResultAutoJoinAge;
 
         long now = System.currentTimeMillis();
 
         String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
-
+        if (lastSelectedConfiguration != null) {
+            age = 14000;
+        }
         // Reset the currentConfiguration Key, and set it only if WifiStateMachine and
         // supplicant agree
         mCurrentConfigurationKey = null;
         WifiConfiguration currentConfiguration = mWifiStateMachine.getCurrentWifiConfiguration();
 
         WifiConfiguration candidate = null;
-
         // Obtain the subset of recently seen networks
         List<WifiConfiguration> list =
-                mWifiConfigStore.getRecentConfiguredNetworks(mScanResultAutoJoinAge, false);
+                mWifiConfigStore.getRecentConfiguredNetworks(age, false);
         if (list == null) {
-            if (VDBG)  logDbg("attemptAutoJoin nothing known=" +
-                    mWifiConfigStore.getconfiguredNetworkSize());
+            if (VDBG) logDbg("attemptAutoJoin nothing known=" +
+                    mWifiConfigStore.getConfiguredNetworksSize());
             return false;
         }
 
         // Find the currently connected network: ask the supplicant directly
-        String val = mWifiNative.status(true);
-        String status[] = val.split("\\r?\\n");
-        if (VDBG) {
-            logDbg("attemptAutoJoin() status=" + val + " split="
-                    + Integer.toString(status.length));
-        }
+        int supplicantNetId = getNetID(mWifiNative.status(true));
 
-        int supplicantNetId = -1;
-        for (String key : status) {
-            if (key.regionMatches(0, "id=", 0, 3)) {
-                int idx = 3;
-                supplicantNetId = 0;
-                while (idx < key.length()) {
-                    char c = key.charAt(idx);
-
-                    if ((c >= 0x30) && (c <= 0x39)) {
-                        supplicantNetId *= 10;
-                        supplicantNetId += c - 0x30;
-                        idx++;
-                    } else {
-                        break;
-                    }
-                }
-            } else if (key.contains("wpa_state=ASSOCIATING")
-                    || key.contains("wpa_state=ASSOCIATED")
-                    || key.contains("wpa_state=FOUR_WAY_HANDSHAKE")
-                    || key.contains("wpa_state=GROUP_KEY_HANDSHAKE")) {
-                if (DBG) {
-                    logDbg("attemptAutoJoin: bail out due to sup state " + key);
-                }
-                // After WifiStateMachine ask the supplicant to associate or reconnect
-                // we might still obtain scan results from supplicant
-                // however the supplicant state in the mWifiInfo and supplicant state tracker
-                // are updated when we get the supplicant state change message which can be
-                // processed after the SCAN_RESULT message, so at this point the framework doesn't
-                // know that supplicant is ASSOCIATING.
-                // A good fix for this race condition would be for the WifiStateMachine to add
-                // a new transient state where it expects to get the supplicant message indicating
-                // that it started the association process and within which critical operations
-                // like autojoin should be deleted.
-
-                // This transient state would remove the need for the roam Wathchdog which
-                // basically does that.
-
-                // At the moment, we just query the supplicant state synchronously with the
-                // mWifiNative.status() command, which allow us to know that
-                // supplicant has started association process, even though we didnt yet get the
-                // SUPPLICANT_STATE_CHANGE message.
-                return false;
-            }
-        }
         if (DBG) {
             String conf = "";
             String last = "";
@@ -1336,35 +1478,8 @@
                     + " ---> suppNetId=" + Integer.toString(supplicantNetId));
         }
 
-        if (currentConfiguration != null) {
-            if (supplicantNetId != currentConfiguration.networkId
-                    // https://b.corp.google.com/issue?id=16484607
-                    // mark this condition as an error only if the mismatched networkId are valid
-                    && supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
-                    && currentConfiguration.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
-                logDbg("attemptAutoJoin() ERROR wpa_supplicant out of sync nid="
-                        + Integer.toString(supplicantNetId) + " WifiStateMachine="
-                        + Integer.toString(currentConfiguration.networkId));
-                mWifiStateMachine.disconnectCommand();
-                return false;
-            } else if (currentConfiguration.ephemeral && (!mAllowUntrustedConnections ||
-                    !haveRecentlySeenScoredBssid(currentConfiguration))) {
-                // The current connection is untrusted (the framework added it), but we're either
-                // no longer allowed to connect to such networks, the score has been nullified
-                // since we connected, or the scored BSSID has gone out of range.
-                // Drop the current connection and perform the rest of autojoin.
-                logDbg("attemptAutoJoin() disconnecting from unwanted ephemeral network");
-                mWifiStateMachine.disconnectCommand(Process.WIFI_UID,
-                        mAllowUntrustedConnections ? 1 : 0);
-                return false;
-            } else {
-                mCurrentConfigurationKey = currentConfiguration.configKey();
-            }
-        } else {
-            if (supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID) {
-                // Maybe in the process of associating, skip this attempt
-                return false;
-            }
+        if (!setCurrentConfigurationKey(currentConfiguration, supplicantNetId)) {
+            return false;
         }
 
         int currentNetId = -1;
@@ -1384,48 +1499,17 @@
                 continue;
             }
 
-            if (config.autoJoinStatus >=
-                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
-                // Wait for 5 minutes before reenabling config that have known,
-                // repeated connection or DHCP failures
-                if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
-                        || config.disableReason
-                        == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
-                        || config.disableReason
-                        == WifiConfiguration.DISABLED_AUTH_FAILURE) {
-                    if (config.blackListTimestamp == 0
-                            || (config.blackListTimestamp > now)) {
-                        // Sanitize the timestamp
-                        config.blackListTimestamp = now;
-                    }
-                    if ((now - config.blackListTimestamp) >
-                            mWifiConfigStore.wifiConfigBlacklistMinTimeMilli) {
-                        // Re-enable the WifiConfiguration
-                        config.status = WifiConfiguration.Status.ENABLED;
+            if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
+                updateBlackListStatus(config, now);
+                continue;
+            }
 
-                        // Reset the blacklist condition
-                        config.numConnectionFailures = 0;
-                        config.numIpConfigFailures = 0;
-                        config.numAuthFailures = 0;
-                        config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
-
-                        config.dirty = true;
-                    } else {
-                        if (VDBG) {
-                            long delay = mWifiConfigStore.wifiConfigBlacklistMinTimeMilli
-                                    - (now - config.blackListTimestamp);
-                            logDbg("attemptautoJoin " + config.configKey()
-                                    + " dont unblacklist yet, waiting for "
-                                    + delay + " ms");
-                        }
-                    }
-                }
-                // Avoid networks disabled because of AUTH failure altogether
+            if (config.userApproved == WifiConfiguration.USER_PENDING ||
+                    config.userApproved == WifiConfiguration.USER_BANNED) {
                 if (DBG) {
-                    logDbg("attemptAutoJoin skip candidate due to auto join status "
-                            + Integer.toString(config.autoJoinStatus) + " key "
-                            + config.configKey(true)
-                            + " reason " + config.disableReason);
+                    logDbg("attemptAutoJoin skip candidate due to user approval status "
+                            + WifiConfiguration.userApprovedAsString(config.userApproved) + " key "
+                            + config.configKey(true));
                 }
                 continue;
             }
@@ -1450,42 +1534,29 @@
                 }
             }
 
+            if (config.visibility == null) {
+                continue;
+            }
+
             // Try to unblacklist based on good visibility
-            if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Soft
-                    && config.visibility.rssi24
-                    < mWifiConfigStore.thresholdUnblacklistThreshold24Soft) {
+            if (underSoftThreshold(config)) {
                 if (DBG) {
-                    logDbg("attemptAutoJoin do not unblacklist due to low visibility "
-                            + config.autoJoinStatus
-                            + " key " + config.configKey(true)
-                            + " rssi=(" + config.visibility.rssi24
-                            + "," + config.visibility.rssi5
-                            + ") num=(" + config.visibility.num24
-                            + "," + config.visibility.num5 + ")");
+                    logDbg("attemptAutoJoin do not unblacklist due to low visibility " +
+                            config.configKey() + " status=" + config.autoJoinStatus);
                 }
-            } else if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Hard
-                    && config.visibility.rssi24
-                    < mWifiConfigStore.thresholdUnblacklistThreshold24Hard) {
+            } else if (underHardThreshold(config)) {
                 // If the network is simply temporary disabled, don't allow reconnect until
                 // RSSI becomes good enough
                 config.setAutoJoinStatus(config.autoJoinStatus - 1);
                 if (DBG) {
-                    logDbg("attemptAutoJoin good candidate seen, bumped soft -> status="
-                            + config.autoJoinStatus
-                            + " " + config.configKey(true) + " rssi=("
-                            + config.visibility.rssi24 + "," + config.visibility.rssi5
-                            + ") num=(" + config.visibility.num24
-                            + "," + config.visibility.num5 + ")");
+                    logDbg("attemptAutoJoin good candidate seen, bumped soft -> status=" +
+                            config.configKey() + " status=" + config.autoJoinStatus);
                 }
             } else {
                 config.setAutoJoinStatus(config.autoJoinStatus - 3);
                 if (DBG) {
-                    logDbg("attemptAutoJoin good candidate seen, bumped hard -> status="
-                            + config.autoJoinStatus
-                            + " " + config.configKey(true) + " rssi=("
-                            + config.visibility.rssi24 + "," + config.visibility.rssi5
-                            + ") num=(" + config.visibility.num24
-                            + "," + config.visibility.num5 + ")");
+                    logDbg("attemptAutoJoin good candidate seen, bumped hard -> status=" +
+                            config.configKey() + " status=" + config.autoJoinStatus);
                 }
             }
 
@@ -1493,12 +1564,8 @@
                     WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED) {
                 // Network is blacklisted, skip
                 if (DBG) {
-                    logDbg("attemptAutoJoin skip blacklisted -> status="
-                            + config.autoJoinStatus
-                            + " " + config.configKey(true) + " rssi=("
-                            + config.visibility.rssi24 + "," + config.visibility.rssi5
-                            + ") num=(" + config.visibility.num24
-                            + "," + config.visibility.num5 + ")");
+                    logDbg("attemptAutoJoin skip blacklisted -> status=" +
+                            config.configKey() + " status=" + config.autoJoinStatus);
                 }
                 continue;
             }
@@ -1517,10 +1584,6 @@
                 isLastSelected = true;
             }
 
-            if (config.visibility == null) {
-                continue;
-            }
-
             if (config.lastRoamingFailure != 0
                     && currentConfiguration != null
                     && (lastSelectedConfiguration == null
@@ -1544,17 +1607,12 @@
             }
 
             int boost = config.autoJoinUseAggressiveJoinAttemptThreshold + weakRssiBailCount;
-            if ((config.visibility.rssi5 + boost)
-                        < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI
-                        && (config.visibility.rssi24 + boost)
-                        < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI) {
+            if (underThreshold(config,
+                    mWifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI.get() - boost,
+                    mWifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI.get() - boost)) {
+
                 if (DBG) {
-                    logDbg("attemptAutoJoin skip due to low visibility -> status="
-                            + config.autoJoinStatus
-                            + " key " + config.configKey(true) + " rssi="
-                            + config.visibility.rssi24 + ", " + config.visibility.rssi5
-                            + " num=" + config.visibility.num24
-                            + ", " + config.visibility.num5);
+                    logDbg("attemptAutoJoin skip due to low visibility " + config.configKey());
                 }
 
                 // Don't try to autojoin a network that is too far but
@@ -1572,6 +1630,7 @@
                     }
                 }
             }
+            // NOTE: If this condition is updated, update NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN.
             if (config.numNoInternetAccessReports > 0
                     && !isLastSelected
                     && !config.validatedInternetAccess) {
@@ -1601,11 +1660,15 @@
             if (candidate == null) {
                 candidate = config;
             } else {
-                if (VDBG)  {
+                if (VDBG) {
                     logDbg("attemptAutoJoin will compare candidate  " + candidate.configKey()
                             + " with " + config.configKey());
                 }
+
                 int order = compareWifiConfigurations(candidate, config);
+                if (VDBG) {
+                    logDbg("attemptAutoJoin compareWifiConfigurations returned " + order);
+                }
 
                 // The lastSelectedConfiguration is the configuration the user has manually selected
                 // thru WifiPicker, or that a 3rd party app asked us to connect to via the
@@ -1619,7 +1682,7 @@
                     // above RSSI/scorer based selection of linked configuration (+/- 50)
                     // by reducing order by -100
                     order = order - 100;
-                    if (VDBG)   {
+                    if (VDBG) {
                         logDbg("     ...and prefers -100 " + candidate.configKey()
                                 + " over " + config.configKey()
                                 + " because it is the last selected -> "
@@ -1632,7 +1695,7 @@
                     // above RSSI/scorer based selection of linked configuration (+/- 50)
                     // by increasing order by +100
                     order = order + 100;
-                    if (VDBG)   {
+                    if (VDBG) {
                         logDbg("     ...and prefers +100 " + config.configKey()
                                 + " over " + candidate.configKey()
                                 + " because it is the last selected -> "
@@ -1660,10 +1723,11 @@
             long nowMs = System.currentTimeMillis();
             int currentScore = -10000;
             // The untrusted network with highest score
-            ScanResult untrustedCandidate = null;
+            ScanDetail untrustedCandidate = null;
             // Look for untrusted scored network only if the current candidate is bad
             if (isBadCandidate(rssi24, rssi5)) {
-                for (ScanResult result : scanResultCache.values()) {
+                for (ScanDetail scanDetail : scanResultCache.values()) {
+                    ScanResult result = scanDetail.getScanResult();
                     // We look only at untrusted networks with a valid SSID
                     // A trusted result would have been looked at thru it's Wificonfiguration
                     if (TextUtils.isEmpty(result.SSID) || !result.untrusted ||
@@ -1686,13 +1750,13 @@
                                 && score > currentScore) {
                             // Highest score: Select this candidate
                             currentScore = score;
-                            untrustedCandidate = result;
+                            untrustedCandidate = scanDetail;
                             if (VDBG) {
                                 logDbg("AutoJoinController: found untrusted candidate "
                                         + result.SSID
-                                + " RSSI=" + result.level
-                                + " freq=" + result.frequency
-                                + " score=" + score);
+                                        + " RSSI=" + result.level
+                                        + " freq=" + result.frequency
+                                        + " score=" + score);
                             }
                         }
                     }
@@ -1705,6 +1769,7 @@
                         mWifiConfigStore.wifiConfigurationFromScanResult(untrustedCandidate);
                 candidate.allowedKeyManagement.set(KeyMgmt.NONE);
                 candidate.ephemeral = true;
+                candidate.dirty = true;
             }
         }
 
@@ -1716,7 +1781,7 @@
                 && currentConfiguration == null
                 && didBailDueToWeakRssi
                 && (mWifiConfigStore.lastUnwantedNetworkDisconnectTimestamp == 0
-                    || lastUnwanted > (1000 * 60 * 60 * 24 * 7))
+                || lastUnwanted > (1000 * 60 * 60 * 24 * 7))
                 ) {
             // We are bailing out of autojoin although we are seeing a weak configuration, and
             // - we didn't find another valid candidate
@@ -1751,7 +1816,7 @@
                     + candidate.configKey()
                     + current
                     + " linked=" + (currentConfiguration != null
-                            && currentConfiguration.isLinked(candidate))
+                    && currentConfiguration.isLinked(candidate))
                     + " : delta="
                     + Integer.toString(networkDelta) + " "
                     + doSwitch);
@@ -1762,7 +1827,7 @@
          * if user is currently streaming voice traffic,
          * then we should not be allowed to switch regardless of the delta
          */
-        if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) {
+        if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) {      // !!! JNo: Here!
             if (mStaStaSupported) {
                 logDbg("mStaStaSupported --> error do nothing now ");
             } else {
@@ -1818,39 +1883,16 @@
                     }
                 }
                 mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
-                            candidate.networkId, networkSwitchType, candidate);
+                        candidate.networkId, networkSwitchType, candidate);
                 found = true;
             }
         }
 
-        if (networkSwitchType == AUTO_JOIN_IDLE) {
+        if (networkSwitchType == AUTO_JOIN_IDLE && !mWifiConfigStore.enableHalBasedPno.get()) {
             String currentBSSID = mWifiStateMachine.getCurrentBSSID();
             // Attempt same WifiConfiguration roaming
             ScanResult roamCandidate =
                     attemptRoam(null, currentConfiguration, mScanResultAutoJoinAge, currentBSSID);
-            /**
-             *  TODO: (post L initial release)
-             *  consider handling linked configurations roaming (i.e. extended Roaming)
-             *  thru the attemptRoam function which makes use of the RSSI roaming threshold.
-             *  At the moment, extended roaming is only handled thru the attemptAutoJoin()
-             *  function which compare configurations.
-             *
-             *  The advantage of making use of attemptRoam function is that this function
-             *  will looks at all the BSSID of each configurations, instead of only looking
-             *  at WifiConfiguration.visibility which keeps trackonly of the RSSI/band of the
-             *  two highest BSSIDs.
-             */
-            // Attempt linked WifiConfiguration roaming
-            /* if (currentConfiguration != null
-                    && currentConfiguration.linkedConfigurations != null) {
-                for (String key : currentConfiguration.linkedConfigurations.keySet()) {
-                    WifiConfiguration link = mWifiConfigStore.getWifiConfiguration(key);
-                    if (link != null) {
-                        roamCandidate = attemptRoam(roamCandidate, link, mScanResultAutoJoinAge,
-                                currentBSSID);
-                    }
-                }
-            }*/
             if (roamCandidate != null && currentBSSID != null
                     && currentBSSID.equals(roamCandidate.BSSID)) {
                 roamCandidate = null;
@@ -1867,12 +1909,87 @@
                 mWifiConnectionStatistics.numAutoRoamAttempt++;
 
                 mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
-                            currentConfiguration.networkId, 1, roamCandidate);
+                        currentConfiguration.networkId, 1, roamCandidate);
                 found = true;
             }
         }
         if (VDBG) logDbg("Done attemptAutoJoin status=" + Integer.toString(networkSwitchType));
         return found;
     }
+
+    private void logDenial(String reason, WifiConfiguration config) {
+        if (!DBG) {
+            return;
+        }
+        logDbg(reason + config.toString());
+    }
+
+    WifiConfiguration getWifiConfiguration(WifiNative.WifiPnoNetwork network) {
+        if (network.configKey != null) {
+            return mWifiConfigStore.getWifiConfiguration(network.configKey);
+        }
+        return null;
+    }
+
+    ArrayList<WifiNative.WifiPnoNetwork> getPnoList(WifiConfiguration current) {
+        int size = -1;
+        ArrayList<WifiNative.WifiPnoNetwork> list = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+        if (mWifiConfigStore.mCachedPnoList != null) {
+            size = mWifiConfigStore.mCachedPnoList.size();
+        }
+
+        if (DBG) {
+            String s = "";
+            if (current != null) {
+                s = " for: " + current.configKey();
+            }
+            Log.e(TAG, " get Pno List total size:" + size + s);
+        }
+        if (current != null) {
+            String configKey = current.configKey();
+            /**
+             * If we are currently associated to a WifiConfiguration then include
+             * only those networks that have a higher priority
+             */
+            for (WifiNative.WifiPnoNetwork network : mWifiConfigStore.mCachedPnoList) {
+                WifiConfiguration config = getWifiConfiguration(network);
+                if (config == null) {
+                    continue;
+                }
+                if (config.autoJoinStatus
+                        >= WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS) {
+                     continue;
+                }
+
+                if (!configKey.equals(network.configKey)) {
+                    int choice = getConnectChoice(config, current, true);
+                    if (choice > 0) {
+                        // config is of higher priority
+                        if (DBG) {
+                            Log.e(TAG, " Pno List adding:" + network.configKey
+                                    + " choice " + choice);
+                        }
+                        list.add(network);
+                        network.rssi_threshold = mWifiConfigStore.thresholdGoodRssi24.get();
+                    }
+                }
+            }
+        } else {
+            for (WifiNative.WifiPnoNetwork network : mWifiConfigStore.mCachedPnoList) {
+                WifiConfiguration config = getWifiConfiguration(network);
+                if (config == null) {
+                    continue;
+                }
+                if (config.autoJoinStatus
+                        >= WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS) {
+                    continue;
+                }
+                list.add(network);
+                network.rssi_threshold = mWifiConfigStore.thresholdGoodRssi24.get();
+            }
+        }
+        return list;
+    }
 }
 
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index 94752d3..652899e 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -16,32 +16,34 @@
 
 package com.android.server.wifi;
 
+import android.app.AppGlobals;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
 import android.net.NetworkInfo.DetailedState;
 import android.net.ProxyInfo;
-import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
+import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.Status;
-import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
-
 import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.WpsResult;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiInfo;
-
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -53,12 +55,24 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.server.LocalServices;
+import com.android.internal.R;
 import com.android.server.net.DelayedDiskWrite;
 import com.android.server.net.IpConfigStore;
-import com.android.internal.R;
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.ANQPData;
+import com.android.server.wifi.hotspot2.AnqpCache;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.SupplicantBridge;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.omadm.MOManager;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
 
-import java.io.BufferedReader;
 import java.io.BufferedInputStream;
+import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.EOFException;
@@ -69,19 +83,31 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
-import java.text.SimpleDateFormat;
-import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.*;
-import java.util.zip.Checksum;
 import java.util.zip.CRC32;
+import java.util.zip.Checksum;
+
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+
 
 /**
  * This class provides the API to manage configured
@@ -131,16 +157,17 @@
 public class WifiConfigStore extends IpConfigStore {
 
     private Context mContext;
-    private static final String TAG = "WifiConfigStore";
+    public static final String TAG = "WifiConfigStore";
     private static final boolean DBG = true;
     private static boolean VDBG = false;
     private static boolean VVDBG = false;
 
     private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
+    private static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp";
+    private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
 
     /* configured networks with network id as the key */
-    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
-            new HashMap<Integer, WifiConfiguration>();
+    private final ConfigurationMap mConfiguredNetworks = new ConfigurationMap();
 
     /* A network id is a unique identifier for a network configured in the
      * supplicant. Network ids are generated when the supplicant reads
@@ -149,8 +176,9 @@
      * that is generated from SSID and security type of the network. A mapping
      * from the generated unique id to network id of the network is needed to
      * map supplicant config to IP configuration. */
-    private HashMap<Integer, Integer> mNetworkIds =
-            new HashMap<Integer, Integer>();
+
+    /* Stores a map of NetworkId to ScanCache */
+    private HashMap<Integer, ScanDetailCache> mScanDetailCaches;
 
     /**
      * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user,
@@ -180,176 +208,159 @@
             "/misc/wifi/autojoinconfig.txt";
 
     /* Network History Keys */
-    private static final String SSID_KEY = "SSID:  ";
-    private static final String CONFIG_KEY = "CONFIG:  ";
-    private static final String CHOICE_KEY = "CHOICE:  ";
-    private static final String LINK_KEY = "LINK:  ";
-    private static final String BSSID_KEY = "BSSID:  ";
-    private static final String BSSID_KEY_END = "/BSSID:  ";
-    private static final String RSSI_KEY = "RSSI:  ";
-    private static final String FREQ_KEY = "FREQ:  ";
-    private static final String DATE_KEY = "DATE:  ";
-    private static final String MILLI_KEY = "MILLI:  ";
-    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
-    private static final String NETWORK_ID_KEY = "ID:  ";
-    private static final String PRIORITY_KEY = "PRIORITY:  ";
-    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
-    private static final String AUTH_KEY = "AUTH:  ";
-    private static final String SEPARATOR_KEY = "\n";
-    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
-    private static final String BSSID_STATUS_KEY = "BSSID_STATUS:  ";
-    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
-    private static final String FAILURE_KEY = "FAILURE:  ";
-    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
-    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
-    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
-    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
-    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
-    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
-    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
-    private static final String FQDN_KEY = "FQDN:  ";
-    private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES:  ";
-    private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES:  ";
-    private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES:  ";
-    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE:  ";
-    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH:  ";
-    private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS:  ";
-    private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS :   ";
-    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 SSID_KEY = "SSID";
+    private static final String CONFIG_KEY = "CONFIG";
+    private static final String CHOICE_KEY = "CHOICE";
+    private static final String LINK_KEY = "LINK";
+    private static final String BSSID_KEY = "BSSID";
+    private static final String BSSID_KEY_END = "/BSSID";
+    private static final String RSSI_KEY = "RSSI";
+    private static final String FREQ_KEY = "FREQ";
+    private static final String DATE_KEY = "DATE";
+    private static final String MILLI_KEY = "MILLI";
+    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI";
+    private static final String NETWORK_ID_KEY = "ID";
+    private static final String PRIORITY_KEY = "PRIORITY";
+    private static final String DEFAULT_GW_KEY = "DEFAULT_GW";
+    private static final String AUTH_KEY = "AUTH";
+    private static final String STATUS_KEY = "AUTO_JOIN_STATUS";
+    private static final String BSSID_STATUS_KEY = "BSSID_STATUS";
+    private static final String SELF_ADDED_KEY = "SELF_ADDED";
+    private static final String FAILURE_KEY = "FAILURE";
+    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD";
+    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION";
+    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY";
+    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY";
+    private static final String UPDATE_UID_KEY = "UPDATE_UID";
+    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS";
+    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON";
+    private static final String FQDN_KEY = "FQDN";
+    private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES";
+    private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES";
+    private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES";
+    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE";
+    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH";
+    private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS";
+    private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS";
+    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 CREATOR_NAME_KEY = "CREATOR_NAME";
+    private static final String UPDATE_NAME_KEY = "UPDATE_NAME";
+    private static final String USER_APPROVED_KEY = "USER_APPROVED";
+    private static final String CREATION_TIME_KEY = "CREATION_TIME";
+    private static final String UPDATE_TIME_KEY = "UPDATE_TIME";
 
-    private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST:  ";
+    private static final String SEPARATOR = ":  ";
+    private static final String NL = "\n";
+
     private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
-            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G:  ";
+            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G";
     private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
-            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G:  ";
+            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G";
     private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
-            = "THRESHOLD_UNBLACKLIST_HARD_5G:  ";
+            = "THRESHOLD_UNBLACKLIST_HARD_5G";
     private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
-            = "THRESHOLD_UNBLACKLIST_SOFT_5G:  ";
+            = "THRESHOLD_UNBLACKLIST_SOFT_5G";
     private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
-            = "THRESHOLD_UNBLACKLIST_HARD_24G:  ";
+            = "THRESHOLD_UNBLACKLIST_HARD_24G";
     private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
-            = "THRESHOLD_UNBLACKLIST_SOFT_24G:  ";
+            = "THRESHOLD_UNBLACKLIST_SOFT_24G";
     private static final String THRESHOLD_GOOD_RSSI_5_KEY
-            = "THRESHOLD_GOOD_RSSI_5:  ";
+            = "THRESHOLD_GOOD_RSSI_5";
     private static final String THRESHOLD_LOW_RSSI_5_KEY
-            = "THRESHOLD_LOW_RSSI_5:  ";
+            = "THRESHOLD_LOW_RSSI_5";
     private static final String THRESHOLD_BAD_RSSI_5_KEY
-            = "THRESHOLD_BAD_RSSI_5:  ";
+            = "THRESHOLD_BAD_RSSI_5";
     private static final String THRESHOLD_GOOD_RSSI_24_KEY
-            = "THRESHOLD_GOOD_RSSI_24:  ";
+            = "THRESHOLD_GOOD_RSSI_24";
     private static final String THRESHOLD_LOW_RSSI_24_KEY
-            = "THRESHOLD_LOW_RSSI_24:  ";
+            = "THRESHOLD_LOW_RSSI_24";
     private static final String THRESHOLD_BAD_RSSI_24_KEY
-            = "THRESHOLD_BAD_RSSI_24:  ";
+            = "THRESHOLD_BAD_RSSI_24";
 
     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
-            = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING:   ";
+            = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING";
     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
-            = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING:   ";
+            = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING";
 
     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY
-            = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS:   ";
+            = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS";
     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY
-            = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS:   ";
+            = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS";
 
     private static final String THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY
-            = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS:   ";
+            = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS";
     private static final String THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY
-            = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS:   ";
+            = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS";
 
     private static final String MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
-            = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS:   ";
+            = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS";
     private static final String MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
-            = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS:   ";
+            = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS";
 
     private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
-            "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW:   ";
+            "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW";
     private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
-            "A_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
+            "A_BAND_PREFERENCE_RSSI_THRESHOLD";
     private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
-            "G_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
+            "G_BAND_PREFERENCE_RSSI_THRESHOLD";
 
     private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
             = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED:   ";
 
     private static final String ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY
-            = "ASSOCIATED_PARTIAL_SCAN_PERIOD:   ";
+            = "ASSOCIATED_PARTIAL_SCAN_PERIOD";
     private static final String ASSOCIATED_FULL_SCAN_BACKOFF_KEY
-            = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD:   ";
+            = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD";
     private static final String ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY
-            = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED:   ";
+            = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED";
     private static final String ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS_KEY
-            = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS:   ";
+            = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS";
 
     private static final String ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY
-            = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED:   ";
+            = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED";
+
+    private static final String ENABLE_HAL_BASED_PNO
+            = "ENABLE_HAL_BASED_PNO";
 
     // The three below configurations are mainly for power stats and CPU usage tracking
     // allowing to incrementally disable framework features
-    private static final String ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY
-            = "ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED:   ";
     private static final String ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY
-            = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED:   ";
+            = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED";
     private static final String ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY
-            = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED:   ";
+            = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED";
     private static final String ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY
-            = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY:   ";
+            = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY";
+
+    public static final String idStringVarName = "id_str";
 
     // The Wifi verbose log is provided as a way to persist the verbose logging settings
     // for testing purpose.
     // It is not intended for normal use.
     private static final String WIFI_VERBOSE_LOGS_KEY
-            = "WIFI_VERBOSE_LOGS:   ";
+            = "WIFI_VERBOSE_LOGS";
 
     // As we keep deleted PSK WifiConfiguration for a while, the PSK of
     // those deleted WifiConfiguration is set to this random unused PSK
     private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
 
-    public boolean enableAutoJoinScanWhenAssociated = true;
-    public boolean enableAutoJoinWhenAssociated = true;
-    public boolean enableChipWakeUpWhenAssociated = true;
-    public boolean enableRssiPollWhenAssociated = true;
-
-    public int maxTxPacketForNetworkSwitching = 40;
-    public int maxRxPacketForNetworkSwitching = 80;
-
     public int maxTxPacketForFullScans = 8;
     public int maxRxPacketForFullScans = 16;
 
     public int maxTxPacketForPartialScans = 40;
     public int maxRxPacketForPartialScans = 80;
 
-    public boolean enableFullBandScanWhenAssociated = true;
-
-    public int thresholdInitialAutoJoinAttemptMin5RSSI
-            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5;
-    public int thresholdInitialAutoJoinAttemptMin24RSSI
-            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24;
-
-    public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5;
-    public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5;
-    public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5;
-    public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24;
-    public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24;
-    public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24;
-
-    public int associatedFullScanBackoff = 12; // Will be divided by 8 by WifiStateMachine
     public int associatedFullScanMaxIntervalMilli = 300000;
 
-    public int associatedPartialScanPeriodMilli;
-
     // Sane value for roam blacklisting (not switching to a network if already associated)
     // 2 days
     public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000;
 
     public int bandPreferenceBoostFactor5 = 5; // Boost by 5 dB per dB above threshold
     public int bandPreferencePenaltyFactor5 = 2; // Penalize by 2 dB per dB below threshold
-    public int bandPreferencePenaltyThreshold5 = WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD;
-    public int bandPreferenceBoostThreshold5 = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD;
 
     public int badLinkSpeed24 = 6;
     public int badLinkSpeed5 = 12;
@@ -367,22 +378,8 @@
     public int associatedHysteresisHigh = +14;
     public int associatedHysteresisLow = +8;
 
-    public int thresholdUnblacklistThreshold5Hard
-            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD;
-    public int thresholdUnblacklistThreshold5Soft
-            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT;
-    public int thresholdUnblacklistThreshold24Hard
-            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD;
-    public int thresholdUnblacklistThreshold24Soft
-            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT;
-    public int enableVerboseLogging = 0;
     boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271
 
-    public int alwaysEnableScansWhileAssociated = 0;
-
-    public int maxNumActiveChannelsForPartialScans = 6;
-    public int maxNumPassiveChannelsForPartialScans = 2;
-
     public boolean roamOnAny = false;
     public boolean onlyLinkSameCredentialConfigurations = true;
 
@@ -395,6 +392,51 @@
 
     public static final int maxNumScanCacheEntries = 128;
 
+    public final AtomicBoolean enableHalBasedPno = new AtomicBoolean(true);
+    public final AtomicBoolean enableSsidWhitelist = new AtomicBoolean(true);
+    public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true);
+    public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true);
+    public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true);
+    public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true);
+    public final AtomicInteger thresholdInitialAutoJoinAttemptMin5RSSI =
+            new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5);
+    public final AtomicInteger thresholdInitialAutoJoinAttemptMin24RSSI =
+            new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24);
+    public final AtomicInteger thresholdUnblacklistThreshold5Hard
+            = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD);
+    public final AtomicInteger thresholdUnblacklistThreshold5Soft
+            = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT);
+    public final AtomicInteger thresholdUnblacklistThreshold24Hard
+            = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD);
+    public final AtomicInteger thresholdUnblacklistThreshold24Soft
+            = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT);
+    public final AtomicInteger thresholdGoodRssi5 =
+            new AtomicInteger(WifiConfiguration.GOOD_RSSI_5);
+    public final AtomicInteger thresholdLowRssi5 = new AtomicInteger(WifiConfiguration.LOW_RSSI_5);
+    public final AtomicInteger thresholdBadRssi5 = new AtomicInteger(WifiConfiguration.BAD_RSSI_5);
+    public final AtomicInteger thresholdGoodRssi24 =
+            new AtomicInteger(WifiConfiguration.GOOD_RSSI_24);
+    public final AtomicInteger thresholdLowRssi24 = new AtomicInteger(WifiConfiguration.LOW_RSSI_24);
+    public final AtomicInteger thresholdBadRssi24 = new AtomicInteger(WifiConfiguration.BAD_RSSI_24);
+    public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40);
+    public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80);
+    public final AtomicInteger enableVerboseLogging = new AtomicInteger(0);
+    public final AtomicInteger bandPreferenceBoostThreshold5 =
+            new AtomicInteger(WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD);
+    public final AtomicInteger associatedFullScanBackoff =
+            new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine
+    public final AtomicInteger bandPreferencePenaltyThreshold5 =
+            new AtomicInteger(WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD);
+    public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0);
+    public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2);
+    public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6);
+    public final AtomicInteger wifiDisconnectedShortScanIntervalMilli = new AtomicInteger(15000);
+    public final AtomicInteger wifiDisconnectedLongScanIntervalMilli = new AtomicInteger(120000);
+    public final AtomicInteger wifiAssociatedShortScanIntervalMilli = new AtomicInteger(20000);
+    public final AtomicInteger wifiAssociatedLongScanIntervalMilli = new AtomicInteger(180000);
+
+    private static final Map<String, Object> sKeyMap = new HashMap<>();
+
     /**
      * Regex pattern for extracting a connect choice.
      * Matches a strings like the following:
@@ -426,7 +468,9 @@
             WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
             WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
             WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
-            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
+            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY,
+            WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY
+    };
 
 
     /**
@@ -460,9 +504,70 @@
      */
     private String lastSelectedConfiguration = null;
 
-    WifiConfigStore(Context c, WifiNative wn) {
+    /**
+     * Cached PNO list, it is updated when WifiConfiguration changes due to user input.
+     */
+    ArrayList<WifiNative.WifiPnoNetwork> mCachedPnoList
+            = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+    /*
+     * BSSID blacklist, i.e. list of BSSID we want to avoid
+     */
+    HashSet<String> mBssidBlacklist = new HashSet<String>();
+
+    /*
+     * Lost config list, whenever we read a config from networkHistory.txt that was not in
+     * wpa_supplicant.conf
+     */
+    HashSet<String> mLostConfigsDbg = new HashSet<String>();
+
+    private final AnqpCache mAnqpCache;
+    private final SupplicantBridge mSupplicantBridge;
+    private final MOManager mMOManager;
+    private final SIMAccessor mSIMAccessor;
+
+    private WifiStateMachine mWifiStateMachine;
+
+    WifiConfigStore(Context c,  WifiStateMachine w, WifiNative wn) {
         mContext = c;
         mWifiNative = wn;
+        mWifiStateMachine = w;
+
+        // A map for value setting in readAutoJoinConfig() - replacing the replicated code.
+        sKeyMap.put(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, enableAutoJoinWhenAssociated);
+        sKeyMap.put(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, enableFullBandScanWhenAssociated);
+        sKeyMap.put(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, enableChipWakeUpWhenAssociated);
+        sKeyMap.put(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, enableRssiPollWhenAssociated);
+        sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, thresholdInitialAutoJoinAttemptMin5RSSI);
+        sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, thresholdInitialAutoJoinAttemptMin24RSSI);
+        sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, thresholdUnblacklistThreshold5Hard);
+        sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, thresholdUnblacklistThreshold5Soft);
+        sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, thresholdUnblacklistThreshold24Hard);
+        sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, thresholdUnblacklistThreshold24Soft);
+        sKeyMap.put(THRESHOLD_GOOD_RSSI_5_KEY, thresholdGoodRssi5);
+        sKeyMap.put(THRESHOLD_LOW_RSSI_5_KEY, thresholdLowRssi5);
+        sKeyMap.put(THRESHOLD_BAD_RSSI_5_KEY, thresholdBadRssi5);
+        sKeyMap.put(THRESHOLD_GOOD_RSSI_24_KEY, thresholdGoodRssi24);
+        sKeyMap.put(THRESHOLD_LOW_RSSI_24_KEY, thresholdLowRssi24);
+        sKeyMap.put(THRESHOLD_BAD_RSSI_24_KEY, thresholdBadRssi24);
+        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxTxPacketForNetworkSwitching);
+        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxRxPacketForNetworkSwitching);
+        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, maxTxPacketForNetworkSwitching);
+        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, maxRxPacketForNetworkSwitching);
+        sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxTxPacketForNetworkSwitching);
+        sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxRxPacketForNetworkSwitching);
+        sKeyMap.put(WIFI_VERBOSE_LOGS_KEY, enableVerboseLogging);
+        sKeyMap.put(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferenceBoostThreshold5);
+        sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
+        sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
+
+        sKeyMap.put(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, associatedFullScanBackoff);
+        sKeyMap.put(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferencePenaltyThreshold5);
+        sKeyMap.put(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, alwaysEnableScansWhileAssociated);
+        sKeyMap.put(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumPassiveChannelsForPartialScans);
+        sKeyMap.put(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumActiveChannelsForPartialScans);
+        sKeyMap.put(ENABLE_HAL_BASED_PNO, enableHalBasedPno);
+        sKeyMap.put(ENABLE_HAL_BASED_PNO, enableSsidWhitelist);
 
         if (showNetworks) {
             mLocalLog = mWifiNative.getLocalLog();
@@ -473,20 +578,25 @@
             mFileObserver = null;
         }
 
-        associatedPartialScanPeriodMilli = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_associated_scan_interval);
-        loge("associatedPartialScanPeriodMilli set to " + associatedPartialScanPeriodMilli);
+        wifiAssociatedShortScanIntervalMilli.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_associated_short_scan_interval));
+        wifiAssociatedLongScanIntervalMilli.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_associated_short_scan_interval));
+        wifiDisconnectedShortScanIntervalMilli.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_disconnected_short_scan_interval));
+        wifiDisconnectedLongScanIntervalMilli.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_disconnected_long_scan_interval));
 
         onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
                 R.bool.config_wifi_only_link_same_credential_configurations);
-        maxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
-        maxNumPassiveChannelsForPartialScans = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels);
+        maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
+        maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels));
         associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_associated_full_scan_max_interval);
-        associatedFullScanBackoff = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_associated_full_scan_backoff);
+        associatedFullScanBackoff.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_associated_full_scan_backoff));
         enableLinkDebouncing = mContext.getResources().getBoolean(
                 R.bool.config_wifi_enable_disconnection_debounce);
 
@@ -498,28 +608,28 @@
         bandPreferencePenaltyFactor5 = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_5GHz_preference_penalty_factor);
 
-        bandPreferencePenaltyThreshold5 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_5GHz_preference_penalty_threshold);
-        bandPreferenceBoostThreshold5 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_5GHz_preference_boost_threshold);
+        bandPreferencePenaltyThreshold5.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_5GHz_preference_penalty_threshold));
+        bandPreferenceBoostThreshold5.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_5GHz_preference_boost_threshold));
 
         associatedHysteresisHigh = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_current_association_hysteresis_high);
         associatedHysteresisLow = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_current_association_hysteresis_low);
 
-        thresholdBadRssi5 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
-        thresholdLowRssi5 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
-        thresholdGoodRssi5 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
-        thresholdBadRssi24 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
-        thresholdLowRssi24 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
-        thresholdGoodRssi24 = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+        thresholdBadRssi5.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
+        thresholdLowRssi5.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
+        thresholdGoodRssi5.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
+        thresholdBadRssi24.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
+        thresholdLowRssi24.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
+        thresholdGoodRssi24.set(mContext.getResources().getInteger(
+                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
 
         enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
                 R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
@@ -540,12 +650,8 @@
         wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_network_black_list_min_time_milli);
 
-
-        enableAutoJoinScanWhenAssociated = mContext.getResources().getBoolean(
-                R.bool.config_wifi_framework_enable_associated_autojoin_scan);
-
-        enableAutoJoinWhenAssociated = mContext.getResources().getBoolean(
-                R.bool.config_wifi_framework_enable_associated_network_selection);
+        enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
+                R.bool.config_wifi_framework_enable_associated_network_selection));
 
         currentNetworkBoost = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_current_network_boost);
@@ -555,10 +661,33 @@
 
         networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger(
                 R.integer.config_wifi_network_switching_blacklist_time);
+
+        enableHalBasedPno.set(mContext.getResources().getBoolean(
+                        R.bool.config_wifi_hal_pno_enable));
+
+        enableSsidWhitelist.set(mContext.getResources().getBoolean(
+                R.bool.config_wifi_ssid_white_list_enable));
+        if (!enableHalBasedPno.get() && enableSsidWhitelist.get()) {
+            enableSsidWhitelist.set(false);
+        }
+
+        boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
+        Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
+
+        mMOManager = new MOManager(new File(PPS_FILE), hs2on);
+        mAnqpCache = new AnqpCache();
+        mSupplicantBridge = new SupplicantBridge(mWifiNative, this);
+        mScanDetailCaches = new HashMap<>();
+
+        mSIMAccessor = new SIMAccessor(mContext);
+    }
+
+    public void trimANQPCache(boolean all) {
+        mAnqpCache.clear(all, DBG);
     }
 
     void enableVerboseLogging(int verbose) {
-        enableVerboseLogging = verbose;
+        enableVerboseLogging.set(verbose);
         if (verbose > 0) {
             VDBG = true;
             showNetworks = true;
@@ -602,7 +731,8 @@
         return mConfiguredNetworks.size();
     }
 
-    private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
+    private List<WifiConfiguration>
+    getConfiguredNetworks(Map<String, String> pskMap) {
         List<WifiConfiguration> networks = new ArrayList<>();
         for(WifiConfiguration config : mConfiguredNetworks.values()) {
             WifiConfiguration newConfig = new WifiConfiguration(config);
@@ -627,6 +757,19 @@
     }
 
     /**
+     * This function returns all configuration, and is used for cebug and creating bug reports.
+     */
+    private List<WifiConfiguration>
+    getAllConfiguredNetworks() {
+        List<WifiConfiguration> networks = new ArrayList<>();
+        for(WifiConfiguration config : mConfiguredNetworks.values()) {
+            WifiConfiguration newConfig = new WifiConfiguration(config);
+            networks.add(newConfig);
+        }
+        return networks;
+    }
+
+    /**
      * Fetch the list of currently configured networks
      * @return List of networks
      */
@@ -644,6 +787,25 @@
     }
 
     /**
+     * Find matching network for this scanResult
+     */
+    WifiConfiguration getMatchingConfig(ScanResult scanResult) {
+
+        for (Map.Entry entry : mScanDetailCaches.entrySet()) {
+            Integer netId = (Integer) entry.getKey();
+            ScanDetailCache cache = (ScanDetailCache) entry.getValue();
+            WifiConfiguration config = getWifiConfiguration(netId);
+            if (config == null)
+                continue;
+            if (cache.get(scanResult.BSSID) != null) {
+                return config;
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Fetch the preSharedKeys for all networks.
      * @return a map from Ssid to preSharedKey.
      */
@@ -651,12 +813,6 @@
         return readNetworkVariablesFromSupplicantFile("psk");
     }
 
-    int getconfiguredNetworkSize() {
-        if (mConfiguredNetworks == null)
-            return 0;
-        return mConfiguredNetworks.size();
-    }
-
     /**
      * Fetch the list of currently configured networks that were recently seen
      *
@@ -674,7 +830,11 @@
             }
 
             // Calculate the RSSI for scan results that are more recent than milli
-            config.setVisibility(milli);
+            ScanDetailCache cache = getScanDetailCache(config);
+            if (cache == null) {
+                continue;
+            }
+            config.setVisibility(cache.getVisibility(milli));
             if (config.visibility == null) {
                 continue;
             }
@@ -696,14 +856,15 @@
      */
     void updateConfiguration(WifiInfo info) {
         WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
-        if (config != null && config.scanResultCache != null) {
-            ScanResult result = config.scanResultCache.get(info.getBSSID());
-            if (result != null) {
+        if (config != null && getScanDetailCache(config) != null) {
+            ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
+            if (scanDetail != null) {
+                ScanResult result = scanDetail.getScanResult();
                 long previousSeen = result.seen;
                 int previousRssi = result.level;
 
                 // Update the scan result
-                result.seen = System.currentTimeMillis();
+                scanDetail.setSeen();
                 result.level = info.getRssi();
 
                 // Average the RSSI value
@@ -725,8 +886,6 @@
      * @return Wificonfiguration
      */
     WifiConfiguration getWifiConfiguration(int netId) {
-        if (mConfiguredNetworks == null)
-            return null;
         return mConfiguredNetworks.get(netId);
     }
 
@@ -735,16 +894,7 @@
      * @return Wificonfiguration
      */
     WifiConfiguration getWifiConfiguration(String key) {
-        if (key == null)
-            return null;
-        int hash = key.hashCode();
-        if (mNetworkIds == null)
-            return null;
-        Integer n = mNetworkIds.get(hash);
-        if (n == null)
-            return null;
-        int netId = n.intValue();
-        return getWifiConfiguration(netId);
+        return mConfiguredNetworks.getByConfigKey(key);
     }
 
     /**
@@ -786,6 +936,7 @@
                     config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
                 } else {
                     loge("Enable network failed on " + config.networkId);
+
                 }
             }
         }
@@ -796,6 +947,26 @@
         }
     }
 
+    private boolean setNetworkPriorityNative(int netId, int priority) {
+        return mWifiNative.setNetworkVariable(netId,
+                WifiConfiguration.priorityVarName, Integer.toString(priority));
+    }
+
+    private boolean setSSIDNative(int netId, String ssid) {
+        return mWifiNative.setNetworkVariable(netId, WifiConfiguration.ssidVarName,
+                encodeSSID(ssid));
+    }
+
+    public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
+        if (config != null) {
+            if (config.lastConnectUid != uid) {
+                config.lastConnectUid = uid;
+                config.dirty = true;
+                return true;
+            }
+        }
+        return false;
+    }
 
     /**
      * Selects the specified network for connection. This involves
@@ -806,34 +977,61 @@
      * a call to enableAllNetworks() needs to be issued upon a connection
      * or a failure event from supplicant
      *
-     * @param netId network to select for connection
+     * @param config network to select for connection
+     * @param updatePriorities makes config highest priority network
      * @return false if the network id is invalid
      */
-    boolean selectNetwork(int netId) {
-        if (VDBG) localLog("selectNetwork", netId);
-        if (netId == INVALID_NETWORK_ID) return false;
+    boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
+        if (VDBG) localLog("selectNetwork", config.networkId);
+        if (config.networkId == INVALID_NETWORK_ID) return false;
 
         // Reset the priority of each network at start or if it goes too high.
         if (mLastPriority == -1 || mLastPriority > 1000000) {
-            for(WifiConfiguration config : mConfiguredNetworks.values()) {
-                if (config.networkId != INVALID_NETWORK_ID) {
-                    config.priority = 0;
-                    addOrUpdateNetworkNative(config, -1);
+            for(WifiConfiguration config2 : mConfiguredNetworks.values()) {
+                if (updatePriorities) {
+                    if (config2.networkId != INVALID_NETWORK_ID) {
+                        config2.priority = 0;
+                        setNetworkPriorityNative(config2.networkId, config.priority);
+                    }
                 }
             }
             mLastPriority = 0;
         }
 
         // Set to the highest priority and save the configuration.
-        WifiConfiguration config = new WifiConfiguration();
-        config.networkId = netId;
-        config.priority = ++mLastPriority;
+        if (updatePriorities) {
+            config.priority = ++mLastPriority;
+            setNetworkPriorityNative(config.networkId, config.priority);
+            buildPnoList();
+        }
 
-        addOrUpdateNetworkNative(config, -1);
-        mWifiNative.saveConfig();
+        if (config.isPasspoint()) {
+            /* need to slap on the SSID of selected bssid to work */
+            if (getScanDetailCache(config).size() != 0) {
+                ScanDetail result = getScanDetailCache(config).getFirst();
+                if (result == null) {
+                    loge("Could not find scan result for " + config.BSSID);
+                } else {
+                    log("Setting SSID for " + config.networkId + " to" + result.getSSID());
+                    setSSIDNative(config.networkId, result.getSSID());
+                    config.SSID = result.getSSID();
+                }
+
+            } else {
+                loge("Could not find bssid for " + config);
+            }
+        }
+
+        if (updatePriorities)
+            mWifiNative.saveConfig();
+        else
+            mWifiNative.selectNetwork(config.networkId);
+
+        updateLastConnectUid(config, uid);
+        writeKnownNetworkHistory(false);
 
         /* Enable the given network while disabling all other networks */
-        enableNetworkWithoutBroadcast(netId, true);
+        enableNetworkWithoutBroadcast(config.networkId, true);
 
        /* Avoid saving the config & sending a broadcast to prevent settings
         * from displaying a disabled list of networks */
@@ -922,11 +1120,11 @@
         if (info != null
             && info.getBSSID() != null
             && ScanResult.is5GHz(info.getFrequency())
-            && info.getRssi() > (bandPreferenceBoostThreshold5 + 3)) {
+            && info.getRssi() > (bandPreferenceBoostThreshold5.get() + 3)) {
             WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
             if (config != null) {
-                if (config.scanResultCache != null) {
-                    ScanResult result = config.scanResultCache.get(info.getBSSID());
+                if (getScanDetailCache(config) != null) {
+                    ScanResult result = getScanDetailCache(config).get(info.getBSSID());
                     if (result != null) {
                         result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED + 1);
                     }
@@ -1017,16 +1215,13 @@
             return null;
         }
 
-        WifiConfiguration foundConfig = null;
+        WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeral(SSID);
 
         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;
-            }
+        if (foundConfig != null) {
+            loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
         }
 
         // Force a write, because the mDeletedEphemeralSSIDs list has changed even though the
@@ -1045,13 +1240,18 @@
     boolean forgetNetwork(int netId) {
         if (showNetworks) localLog("forgetNetwork", netId);
 
+        WifiConfiguration config = mConfiguredNetworks.get(netId);
         boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
         if (!remove) {
             //success but we dont want to remove the network from supplicant conf file
             return true;
         }
         if (mWifiNative.removeNetwork(netId)) {
+            if (config != null && config.isPasspoint()) {
+                writePasspointConfigs(config.FQDN, null);
+            }
             mWifiNative.saveConfig();
+            writeKnownNetworkHistory(true);
             return true;
         } else {
             loge("Failed to remove network " + netId);
@@ -1074,6 +1274,14 @@
         Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
                 + " uid=" + Integer.toString(config.creatorUid)
                 + "/" + Integer.toString(config.lastUpdateUid));
+
+        if (config.isPasspoint()) {
+            /* create a temporary SSID with providerFriendlyName */
+            Long csum = getChecksum(config.FQDN);
+            config.SSID = csum.toString();
+            config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
+        }
+
         NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
         if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
             WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
@@ -1083,9 +1291,103 @@
                             WifiManager.CHANGE_REASON_CONFIG_CHANGE);
             }
         }
+
         return result.getNetworkId();
     }
 
+
+    /**
+     * Get the Wifi PNO list
+     *
+     * @return list of WifiNative.WifiPnoNetwork
+     */
+    private void buildPnoList() {
+        mCachedPnoList = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+        ArrayList<WifiConfiguration> sortedWifiConfigurations
+                = new ArrayList<WifiConfiguration>(getConfiguredNetworks());
+        Log.e(TAG, "buildPnoList sortedWifiConfigurations size " + sortedWifiConfigurations.size());
+        if (sortedWifiConfigurations.size() != 0) {
+            // Sort by descending priority
+            Collections.sort(sortedWifiConfigurations, new Comparator<WifiConfiguration>() {
+                public int compare(WifiConfiguration a, WifiConfiguration b) {
+                    return a.priority >= b.priority ? 1 : -1;
+                }
+            });
+        }
+
+        for (WifiConfiguration config : sortedWifiConfigurations) {
+            // Initialize the RSSI threshold with sane value:
+            // Use the 2.4GHz threshold since most WifiConfigurations are dual bands
+            // There is very little penalty with triggering too soon, i.e. if PNO finds a network
+            // that has an RSSI too low for us to attempt joining it.
+            int threshold = thresholdInitialAutoJoinAttemptMin24RSSI.get();
+            Log.e(TAG, "found sortedWifiConfigurations : " + config.configKey());
+            WifiNative.WifiPnoNetwork network = mWifiNative.new WifiPnoNetwork(config, threshold);
+            mCachedPnoList.add(network);
+        }
+    }
+
+    String[] getWhiteListedSsids(WifiConfiguration config) {
+        int num_ssids = 0;
+        String nonQuoteSSID;
+        int length;
+        if (enableSsidWhitelist.get() == false)
+            return null;
+        List<String> list = new ArrayList<String>();
+        if (config == null)
+            return null;
+        if (config.linkedConfigurations == null) {
+            return null;
+        }
+        if (config.SSID == null || TextUtils.isEmpty(config.SSID)) {
+            return null;
+        }
+        for (String configKey : config.linkedConfigurations.keySet()) {
+
+            // Sanity check that the linked configuration is still valid
+            WifiConfiguration link = getWifiConfiguration(configKey);
+            if (link == null) {
+                continue;
+            }
+
+            if (link.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
+                continue;
+            }
+
+            if (link.hiddenSSID == true) {
+                continue;
+            }
+
+            if (link.SSID == null || TextUtils.isEmpty(link.SSID)) {
+                continue;
+            }
+
+            length = link.SSID.length();
+            if (length > 2 && (link.SSID.charAt(0) == '"') && link.SSID.charAt(length - 1) == '"') {
+                nonQuoteSSID = link.SSID.substring(1, length - 1);
+            } else {
+                nonQuoteSSID = link.SSID;
+            }
+
+            list.add(nonQuoteSSID);
+        }
+
+        if (list.size() != 0) {
+            length = config.SSID.length();
+            if (length > 2 && (config.SSID.charAt(0) == '"')
+                    && config.SSID.charAt(length - 1) == '"') {
+                nonQuoteSSID = config.SSID.substring(1, length - 1);
+            } else {
+                nonQuoteSSID = config.SSID;
+            }
+
+            list.add(nonQuoteSSID);
+        }
+
+        return (String[])list.toArray(new String[0]);
+    }
+
     /**
      * Remove a network. Note that there is no saveConfig operation.
      * This function is retained for compatibility with the public
@@ -1097,13 +1399,24 @@
      */
     boolean removeNetwork(int netId) {
         if (showNetworks) localLog("removeNetwork", netId);
+        WifiConfiguration config = mConfiguredNetworks.get(netId);
         boolean ret = mWifiNative.removeNetwork(netId);
         if (ret) {
             removeConfigAndSendBroadcastIfNeeded(netId);
+            if (config != null && config.isPasspoint()) {
+                writePasspointConfigs(config.FQDN, null);
+            }
         }
         return ret;
     }
 
+
+    static private Long getChecksum(String source) {
+        Checksum csum = new CRC32();
+        csum.update(source.getBytes(), 0, source.getBytes().length);
+        return csum.getValue();
+    }
+
     private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
         if (config != null) {
@@ -1126,20 +1439,23 @@
                     || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
                 if (!TextUtils.isEmpty(config.SSID)) {
                     /* Remember that we deleted this PSK SSID */
-                    Checksum csum = new CRC32();
                     if (config.SSID != null) {
-                        csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);
-                        mDeletedSSIDs.add(csum.getValue());
+                        Long csum = getChecksum(config.SSID);
+                        mDeletedSSIDs.add(csum);
+                        loge("removeNetwork " + Integer.toString(netId)
+                                + " key=" + config.configKey()
+                                + " config.id=" + Integer.toString(config.networkId)
+                                + "  crc=" + csum);
+                    } else {
+                        loge("removeNetwork " + Integer.toString(netId)
+                                + " key=" + config.configKey()
+                                + " config.id=" + Integer.toString(config.networkId));
                     }
-                    loge("removeNetwork " + Integer.toString(netId)
-                            + " key=" + config.configKey()
-                            + " config.id=" + Integer.toString(config.networkId)
-                            + "  crc=" + csum.getValue());
                 }
             }
 
             mConfiguredNetworks.remove(netId);
-            mNetworkIds.remove(configKey(config));
+            mScanDetailCaches.remove(netId);
 
             writeIpAndProxyConfigurations();
             sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
@@ -1148,6 +1464,57 @@
         return true;
     }
 
+    /*
+     * Remove all networks associated with an application
+     *
+     * @param packageName name of the package of networks to remove
+     * @return {@code true} if all networks removed successfully, {@code false} otherwise
+     */
+    boolean removeNetworksForApp(ApplicationInfo app) {
+        if (app == null || app.packageName == null) {
+            return false;
+        }
+
+        boolean success = true;
+
+        WifiConfiguration [] copiedConfigs =
+                mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
+        for (WifiConfiguration config : copiedConfigs) {
+            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
+                continue;
+            }
+            if (showNetworks) {
+                localLog("Removing network " + config.SSID
+                         + ", application \"" + app.packageName + "\" uninstalled"
+                         + " from user " + UserHandle.getUserId(app.uid));
+            }
+            success &= removeNetwork(config.networkId);
+        }
+
+        mWifiNative.saveConfig();
+
+        return success;
+    }
+
+    boolean removeNetworksForUser(int userId) {
+        boolean success = true;
+
+        WifiConfiguration[] copiedConfigs =
+                mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
+        for (WifiConfiguration config : copiedConfigs) {
+            if (userId != UserHandle.getUserId(config.creatorUid)) {
+                continue;
+            }
+            success &= removeNetwork(config.networkId);
+            if (showNetworks) {
+                localLog("Removing network " + config.SSID
+                        + ", user " + userId + " removed");
+            }
+        }
+
+        return success;
+    }
+
     /**
      * Enable a network. Note that there is no saveConfig operation.
      * This function is retained for compatibility with the public
@@ -1157,15 +1524,17 @@
      * @param netId network to be enabled
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
-    boolean enableNetwork(int netId, boolean disableOthers) {
+    boolean enableNetwork(int netId, boolean disableOthers, int uid) {
         boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
         if (disableOthers) {
-            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
+            if (VDBG) localLog("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId);
+            updateLastConnectUid(getWifiConfiguration(netId), uid);
+            writeKnownNetworkHistory(false);
             sendConfiguredNetworksChangedBroadcast();
         } else {
             if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
-            WifiConfiguration enabledNetwork = null;
-            synchronized(mConfiguredNetworks) {
+            WifiConfiguration enabledNetwork;
+            synchronized(mConfiguredNetworks) {                     // !!! Useless synchronization!
                 enabledNetwork = mConfiguredNetworks.get(netId);
             }
             // check just in case the network was removed by someone else.
@@ -1192,14 +1561,12 @@
     void disableAllNetworks() {
         if (VDBG) localLog("disableAllNetworks");
         boolean networkDisabled = false;
-        for(WifiConfiguration config : mConfiguredNetworks.values()) {
-            if(config != null && config.status != Status.DISABLED) {
-                if(mWifiNative.disableNetwork(config.networkId)) {
-                    networkDisabled = true;
-                    config.status = Status.DISABLED;
-                } else {
-                    loge("Disable network failed on " + config.networkId);
-                }
+        for (WifiConfiguration enabled : mConfiguredNetworks.getEnabledNetworks()) {
+            if(mWifiNative.disableNetwork(enabled.networkId)) {
+                networkDisabled = true;
+                enabled.status = Status.DISABLED;
+            } else {
+                loge("Disable network failed on " + enabled.networkId);
             }
         }
 
@@ -1213,7 +1580,11 @@
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean disableNetwork(int netId) {
-        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
+        boolean ret = disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
+        if (ret) {
+            mWifiStateMachine.registerNetworkDisabled(netId);
+        }
+        return ret;
     }
 
     /**
@@ -1359,7 +1730,7 @@
 
     /**
      * Fetch the proxy properties for a given network id
-     * @param network id
+     * @param netId id
      * @return ProxyInfo for the network id
      */
     ProxyInfo getProxyProperties(int netId) {
@@ -1372,7 +1743,7 @@
 
     /**
      * Return if the specified network is using static IP
-     * @param network id
+     * @param netId id
      * @return {@code true} if using static ip for netId
      */
     boolean isUsingStaticIp(int netId) {
@@ -1383,6 +1754,11 @@
         return false;
     }
 
+    boolean isEphemeral(int netId) {
+        WifiConfiguration config = mConfiguredNetworks.get(netId);
+        return config != null && config.ephemeral;
+    }
+
     /**
      * Should be called when a single network configuration is made.
      * @param network The network configuration that changed.
@@ -1414,7 +1790,6 @@
         mLastPriority = 0;
 
         mConfiguredNetworks.clear();
-        mNetworkIds.clear();
 
         int last_id = -1;
         boolean done = false;
@@ -1474,12 +1849,11 @@
                 config.setIpAssignment(IpAssignment.DHCP);
                 config.setProxySettings(ProxySettings.NONE);
 
-                if (mNetworkIds.containsKey(configKey(config))) {
+                if (mConfiguredNetworks.getByConfigKey(config.configKey()) != null) {
                     // That SSID is already known, just ignore this duplicate entry
                     if (showNetworks) localLog("discarded duplicate network ", config.networkId);
-                } else if(config.isValid()){
+                } else if(WifiServiceImpl.isValid(config)){
                     mConfiguredNetworks.put(config.networkId, config);
-                    mNetworkIds.put(configKey(config), config.networkId);
                     if (showNetworks) localLog("loaded configured network", config.networkId);
                 } else {
                     if (showNetworks) log("Ignoring loaded configured for network " + config.networkId
@@ -1490,40 +1864,48 @@
             done = (lines.length == 1);
         }
 
+        readPasspointConfig();
         readIpAndProxyConfigurations();
         readNetworkHistory();
         readAutoJoinConfig();
 
+        buildPnoList();
+
         sendConfiguredNetworksChangedBroadcast();
 
-        if (showNetworks) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
+        if (showNetworks) localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.size() + " networks");
 
-        if (mNetworkIds.size() == 0) {
-            // no networks? Lets log if the wpa_supplicant.conf file contents
-            BufferedReader reader = null;
+        if (mConfiguredNetworks.isEmpty()) {
+            // no networks? Lets log if the file contents
+            logKernelTime();
+            logContents(SUPPLICANT_CONFIG_FILE);
+            logContents(SUPPLICANT_CONFIG_FILE_BACKUP);
+            logContents(networkHistoryConfigFile);
+        }
+    }
+
+    private void logContents(String file) {
+        localLog("--- Begin " + file + " ---", true);
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+                localLog(line, true);
+            }
+        } catch (FileNotFoundException e) {
+            localLog("Could not open " + file + ", " + e, true);
+        } catch (IOException e) {
+            localLog("Could not read " + file + ", " + e, true);
+        } finally {
             try {
-                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
-                if (DBG) {
-                    localLog("--- Begin wpa_supplicant.conf Contents ---", true);
-                    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
-                        localLog(line, true);
-                    }
-                    localLog("--- End wpa_supplicant.conf Contents ---", true);
+                if (reader != null) {
+                    reader.close();
                 }
-            } catch (FileNotFoundException e) {
-                localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
             } catch (IOException e) {
-                localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
-            } finally {
-                try {
-                    if (reader != null) {
-                        reader.close();
-                    }
-                } catch (IOException e) {
-                    // Just ignore the fact that we couldn't close
-                }
+                // Just ignore the fact that we couldn't close
             }
         }
+        localLog("--- End " + file + " Contents ---", true);
     }
 
     private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
@@ -1627,6 +2009,37 @@
         return false;
     }
 
+    void readPasspointConfig() {
+
+        List<HomeSP> homeSPs;
+        try {
+            homeSPs = mMOManager.loadAllSPs();
+        } catch (IOException e) {
+            loge("Could not read " + PPS_FILE + " : " + e);
+            return;
+        }
+
+        mConfiguredNetworks.populatePasspointData(homeSPs, mWifiNative);
+    }
+
+    public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
+        mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
+            @Override
+            public void onWriteCalled(DataOutputStream out) throws IOException {
+                try {
+                    if (homeSP != null) {
+                        mMOManager.addSP(homeSP);
+                    }
+                    else {
+                        mMOManager.removeSP(fqdn);
+                    }
+                } catch (IOException e) {
+                    loge("Could not write " + PPS_FILE + " : " + e);
+                }
+            }
+        }, false);
+    }
+
     public void writeKnownNetworkHistory(boolean force) {
         boolean needUpdate = force;
 
@@ -1675,7 +2088,7 @@
                                 + " nid:" + Integer.toString(config.networkId));
                     }
 
-                    if (config.isValid() == false)
+                    if (!WifiServiceImpl.isValid(config))
                         continue;
 
                     if (config.SSID == null) {
@@ -1687,133 +2100,142 @@
                     if (VDBG) {
                         loge("writeKnownNetworkHistory write config " + config.configKey());
                     }
-                    out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
+                    out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL);
 
-                    out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
-                    out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
-
-                    out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
-                    out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
-                            + Integer.toString(config.disableReason)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY
-                            + Integer.toString(config.numNoInternetAccessReports)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY
-                            + Boolean.toString(config.validatedInternetAccess)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(EPHEMERAL_KEY
-                            + Boolean.toString(config.ephemeral)
-                            + SEPARATOR_KEY);
-                    if (config.peerWifiConfiguration != null) {
-                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
-                                + SEPARATOR_KEY);
+                    if (config.SSID != null) {
+                        out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL);
                     }
-                    out.writeUTF(NUM_CONNECTION_FAILURES_KEY
-                            + Integer.toString(config.numConnectionFailures)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(NUM_AUTH_FAILURES_KEY
-                            + Integer.toString(config.numAuthFailures)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY
-                            + Integer.toString(config.numIpConfigFailures)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
-                            + Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(NUM_ASSOCIATION_KEY
-                            + Integer.toString(config.numAssociation)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(JOIN_ATTEMPT_BOOST_KEY
-                            + Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)
-                            + SEPARATOR_KEY);
-                    //out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
-                    //        + SEPARATOR_KEY);
-                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
-                            + SEPARATOR_KEY);
-                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
-                            + SEPARATOR_KEY);
+                    if (config.FQDN != null) {
+                        out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL);
+                    }
+
+                    out.writeUTF(PRIORITY_KEY + SEPARATOR +
+                            Integer.toString(config.priority) + NL);
+                    out.writeUTF(STATUS_KEY + SEPARATOR +
+                            Integer.toString(config.autoJoinStatus) + NL);
+                    out.writeUTF(SUPPLICANT_STATUS_KEY + SEPARATOR +
+                            Integer.toString(config.status) + NL);
+                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY + SEPARATOR +
+                            Integer.toString(config.disableReason) + NL);
+                    out.writeUTF(NETWORK_ID_KEY + SEPARATOR +
+                            Integer.toString(config.networkId) + NL);
+                    out.writeUTF(SELF_ADDED_KEY + SEPARATOR +
+                            Boolean.toString(config.selfAdded) + NL);
+                    out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR +
+                            Boolean.toString(config.didSelfAdd) + NL);
+                    out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR +
+                            Integer.toString(config.numNoInternetAccessReports) + NL);
+                    out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR +
+                            Boolean.toString(config.validatedInternetAccess) + NL);
+                    out.writeUTF(EPHEMERAL_KEY + SEPARATOR +
+                            Boolean.toString(config.ephemeral) + NL);
+                    if (config.creationTime != null) {
+                        out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL);
+                    }
+                    if (config.updateTime != null) {
+                        out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL);
+                    }
+                    if (config.peerWifiConfiguration != null) {
+                        out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR +
+                                config.peerWifiConfiguration + NL);
+                    }
+                    out.writeUTF(NUM_CONNECTION_FAILURES_KEY + SEPARATOR +
+                            Integer.toString(config.numConnectionFailures) + NL);
+                    out.writeUTF(NUM_AUTH_FAILURES_KEY + SEPARATOR +
+                            Integer.toString(config.numAuthFailures) + NL);
+                    out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY + SEPARATOR +
+                            Integer.toString(config.numIpConfigFailures) + NL);
+                    out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR +
+                            Integer.toString(config.numScorerOverride) + NL);
+                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR +
+                            Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL);
+                    out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR +
+                            Integer.toString(config.numAssociation) + NL);
+                    out.writeUTF(JOIN_ATTEMPT_BOOST_KEY + SEPARATOR +
+                            Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)+ NL);
+                    //out.writeUTF(BLACKLIST_MILLI_KEY + SEPARATOR +
+                    // Long.toString(config.blackListTimestamp) + NL);
+                    out.writeUTF(CREATOR_UID_KEY + SEPARATOR +
+                            Integer.toString(config.creatorUid) + NL);
+                    out.writeUTF(CONNECT_UID_KEY + SEPARATOR +
+                            Integer.toString(config.lastConnectUid) + NL);
+                    out.writeUTF(UPDATE_UID_KEY + SEPARATOR +
+                            Integer.toString(config.lastUpdateUid) + NL);
+                    out.writeUTF(CREATOR_NAME_KEY + SEPARATOR +
+                            config.creatorName + NL);
+                    out.writeUTF(UPDATE_NAME_KEY + SEPARATOR +
+                            config.lastUpdateName + NL);
+                    out.writeUTF(USER_APPROVED_KEY + SEPARATOR +
+                            Integer.toString(config.userApproved) + NL);
                     String allowedKeyManagementString =
                             makeString(config.allowedKeyManagement,
                                     WifiConfiguration.KeyMgmt.strings);
-                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
+                    out.writeUTF(AUTH_KEY + SEPARATOR +
+                            allowedKeyManagementString + NL);
 
                     if (config.connectChoices != null) {
                         for (String key : config.connectChoices.keySet()) {
                             Integer choice = config.connectChoices.get(key);
-                            out.writeUTF(CHOICE_KEY + key + "="
-                                    + choice.toString() + SEPARATOR_KEY);
+                            out.writeUTF(CHOICE_KEY + SEPARATOR +
+                                    key + "=" + choice.toString() + NL);
                         }
                     }
                     if (config.linkedConfigurations != null) {
-                        loge("writeKnownNetworkHistory write linked "
+                        log("writeKnownNetworkHistory write linked "
                                 + config.linkedConfigurations.size());
 
                         for (String key : config.linkedConfigurations.keySet()) {
-                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
+                            out.writeUTF(LINK_KEY + SEPARATOR + key + NL);
                         }
                     }
 
                     String macAddress = config.defaultGwMacAddress;
                     if (macAddress != null) {
-                        out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
+                        out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL);
                     }
 
-                    if (config.scanResultCache != null) {
-                        for (ScanResult result : config.scanResultCache.values()) {
-                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
+                    if (getScanDetailCache(config) != null) {
+                        for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+                            ScanResult result = scanDetail.getScanResult();
+                            out.writeUTF(BSSID_KEY + SEPARATOR +
+                                    result.BSSID + NL);
 
-                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
-                                    + SEPARATOR_KEY);
+                            out.writeUTF(FREQ_KEY + SEPARATOR +
+                                    Integer.toString(result.frequency) + NL);
 
-                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
-                                    + SEPARATOR_KEY);
+                            out.writeUTF(RSSI_KEY + SEPARATOR +
+                                    Integer.toString(result.level) + NL);
 
-                            out.writeUTF(BSSID_STATUS_KEY
-                                    + Integer.toString(result.autoJoinStatus)
-                                    + SEPARATOR_KEY);
+                            out.writeUTF(BSSID_STATUS_KEY + SEPARATOR +
+                                    Integer.toString(result.autoJoinStatus) + NL);
 
                             //if (result.seen != 0) {
-                            //    out.writeUTF(MILLI_KEY + Long.toString(result.seen)
-                            //            + SEPARATOR_KEY);
+                            //    out.writeUTF(MILLI_KEY + SEPARATOR + Long.toString(result.seen)
+                            //            + NL);
                             //}
-                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
+                            out.writeUTF(BSSID_KEY_END + NL);
                         }
                     }
                     if (config.lastFailure != null) {
-                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
+                        out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL);
                     }
-                    out.writeUTF(SEPARATOR_KEY);
+                    out.writeUTF(NL);
                     // Add extra blank lines for clarity
-                    out.writeUTF(SEPARATOR_KEY);
-                    out.writeUTF(SEPARATOR_KEY);
+                    out.writeUTF(NL);
+                    out.writeUTF(NL);
                 }
                 if (mDeletedSSIDs != null && mDeletedSSIDs.size() > 0) {
                     for (Long i : mDeletedSSIDs) {
                         out.writeUTF(DELETED_CRC32_KEY);
                         out.writeUTF(String.valueOf(i));
-                        out.writeUTF(SEPARATOR_KEY);
+                        out.writeUTF(NL);
                     }
                 }
                 if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) {
                     for (String ssid : mDeletedEphemeralSSIDs) {
                         out.writeUTF(DELETED_EPHEMERAL_KEY);
                         out.writeUTF(ssid);
-                        out.writeUTF(SEPARATOR_KEY);
+                        out.writeUTF(NL);
                     }
                 }
             }
@@ -1857,720 +2279,279 @@
         if (showNetworks) {
             localLog("readNetworkHistory() path:" + networkHistoryConfigFile);
         }
-        DataInputStream in = null;
-        try {
-            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
-                    networkHistoryConfigFile)));
+
+        try (DataInputStream in =
+                     new DataInputStream(new BufferedInputStream(
+                             new FileInputStream(networkHistoryConfigFile)))) {
+
+            String bssid = null;
+            String ssid = null;
+
+            int freq = 0;
+            int status = 0;
+            long seen = 0;
+            int rssi = WifiConfiguration.INVALID_RSSI;
+            String caps = null;
+
             WifiConfiguration config = null;
             while (true) {
-                int id = -1;
-                String key = in.readUTF();
-                String bssid = null;
-                String ssid = null;
+                String line = in.readUTF();
+                if (line == null) {
+                    break;
+                }
+                int colon = line.indexOf(':');
+                if (colon < 0) {
+                    continue;
+                }
 
-                int freq = 0;
-                int status = 0;
-                long seen = 0;
-                int rssi = WifiConfiguration.INVALID_RSSI;
-                String caps = null;
-                if (key.startsWith(CONFIG_KEY)) {
+                String key = line.substring(0, colon).trim();
+                String value = line.substring(colon + 1).trim();
 
-                    if (config != null) {
-                        config = null;
-                    }
-                    String configKey = key.replace(CONFIG_KEY, "");
-                    configKey = configKey.replace(SEPARATOR_KEY, "");
-                    // get the networkId for that config Key
-                    Integer n = mNetworkIds.get(configKey.hashCode());
+                if (key.equals(CONFIG_KEY)) {
+
+                    config = mConfiguredNetworks.getByConfigKey(value);
+                    
                     // skip reading that configuration data
                     // since we don't have a corresponding network ID
-                    if (n == null) {
-                        localLog("readNetworkHistory didnt find netid for hash="
-                                + Integer.toString(configKey.hashCode())
-                                + " key: " + configKey);
-                        continue;
-                    }
-                    config = mConfiguredNetworks.get(n);
                     if (config == null) {
-                        localLog("readNetworkHistory didnt find config for netid="
-                                + n.toString()
-                                + " key: " + configKey);
-                    }
-                    status = 0;
-                    ssid = null;
-                    bssid = null;
-                    freq = 0;
-                    seen = 0;
-                    rssi = WifiConfiguration.INVALID_RSSI;
-                    caps = null;
+                        localLog("readNetworkHistory didnt find netid for hash="
+                                + Integer.toString(value.hashCode())
+                                + " key: " + value);
+                        mLostConfigsDbg.add(value);
+                        continue;
+                    } else {
+                        // After an upgrade count old connections as owned by system
+                        if (config.creatorName == null || config.lastUpdateName == null) {
+                            config.creatorName =
+                                mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
+                            config.lastUpdateName = config.creatorName;
 
+                            if (DBG) Log.w(TAG, "Upgrading network " + config.networkId
+                                    + " to " + config.creatorName);
+                        }
+                    }
                 } else if (config != null) {
-                    if (key.startsWith(SSID_KEY)) {
-                        ssid = key.replace(SSID_KEY, "");
-                        ssid = ssid.replace(SEPARATOR_KEY, "");
-                        if (config.SSID != null && !config.SSID.equals(ssid)) {
-                            loge("Error parsing network history file, mismatched SSIDs");
-                            config = null; //error
-                            ssid = null;
-                        } else {
-                            config.SSID = ssid;
-                        }
-                    }
-
-                    if (key.startsWith(FQDN_KEY)) {
-                        String fqdn = key.replace(FQDN_KEY, "");
-                        fqdn = fqdn.replace(SEPARATOR_KEY, "");
-                        config.FQDN = fqdn;
-                    }
-
-                    if (key.startsWith(DEFAULT_GW_KEY)) {
-                        String gateway = key.replace(DEFAULT_GW_KEY, "");
-                        gateway = gateway.replace(SEPARATOR_KEY, "");
-                        config.defaultGwMacAddress = gateway;
-                    }
-
-                    if (key.startsWith(STATUS_KEY)) {
-                        String st = key.replace(STATUS_KEY, "");
-                        st = st.replace(SEPARATOR_KEY, "");
-                        config.autoJoinStatus = Integer.parseInt(st);
-                    }
-
-                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
-                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
-                        reason = reason.replace(SEPARATOR_KEY, "");
-                        config.disableReason = Integer.parseInt(reason);
-                    }
-
-                    if (key.startsWith(SELF_ADDED_KEY)) {
-                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
-                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
-                        config.selfAdded = Boolean.parseBoolean(selfAdded);
-                    }
-
-                    if (key.startsWith(DID_SELF_ADD_KEY)) {
-                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
-                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
-                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
-                    }
-
-                    if (key.startsWith(NO_INTERNET_ACCESS_REPORTS_KEY)) {
-                        String access = key.replace(NO_INTERNET_ACCESS_REPORTS_KEY, "");
-                        access = access.replace(SEPARATOR_KEY, "");
-                        config.numNoInternetAccessReports = Integer.parseInt(access);
-                    }
-
-                    if (key.startsWith(VALIDATED_INTERNET_ACCESS_KEY)) {
-                        String access = key.replace(VALIDATED_INTERNET_ACCESS_KEY, "");
-                        access = access.replace(SEPARATOR_KEY, "");
-                        config.validatedInternetAccess = Boolean.parseBoolean(access);
-                    }
-
-                    if (key.startsWith(EPHEMERAL_KEY)) {
-                        String access = key.replace(EPHEMERAL_KEY, "");
-                        access = access.replace(SEPARATOR_KEY, "");
-                        config.ephemeral = Boolean.parseBoolean(access);
-                    }
-
-                    if (key.startsWith(CREATOR_UID_KEY)) {
-                        String uid = key.replace(CREATOR_UID_KEY, "");
-                        uid = uid.replace(SEPARATOR_KEY, "");
-                        config.creatorUid = Integer.parseInt(uid);
-                    }
-
-                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
-                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
-                        milli = milli.replace(SEPARATOR_KEY, "");
-                        config.blackListTimestamp = Long.parseLong(milli);
-                    }
-
-                    if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
-                        String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numConnectionFailures = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(NUM_IP_CONFIG_FAILURES_KEY)) {
-                        String num = key.replace(NUM_IP_CONFIG_FAILURES_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numIpConfigFailures = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(NUM_AUTH_FAILURES_KEY)) {
-                        String num = key.replace(NUM_AUTH_FAILURES_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numIpConfigFailures = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(SCORER_OVERRIDE_KEY)) {
-                        String num = key.replace(SCORER_OVERRIDE_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numScorerOverride = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
-                        String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(NUM_ASSOCIATION_KEY)) {
-                        String num = key.replace(NUM_ASSOCIATION_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.numAssociation = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(JOIN_ATTEMPT_BOOST_KEY)) {
-                        String num = key.replace(JOIN_ATTEMPT_BOOST_KEY, "");
-                        num = num.replace(SEPARATOR_KEY, "");
-                        config.autoJoinUseAggressiveJoinAttemptThreshold = Integer.parseInt(num);
-                    }
-
-                    if (key.startsWith(CONNECT_UID_KEY)) {
-                        String uid = key.replace(CONNECT_UID_KEY, "");
-                        uid = uid.replace(SEPARATOR_KEY, "");
-                        config.lastConnectUid = Integer.parseInt(uid);
-                    }
-
-                    if (key.startsWith(UPDATE_UID_KEY)) {
-                        String uid = key.replace(UPDATE_UID_KEY, "");
-                        uid = uid.replace(SEPARATOR_KEY, "");
-                        config.lastUpdateUid = Integer.parseInt(uid);
-                    }
-
-                    if (key.startsWith(FAILURE_KEY)) {
-                        config.lastFailure = key.replace(FAILURE_KEY, "");
-                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
-                    }
-
-                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
-                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
-                        config.peerWifiConfiguration =
-                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
-                    }
-
-                    if (key.startsWith(CHOICE_KEY)) {
-                        String choiceStr = key.replace(CHOICE_KEY, "");
-                        choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
-                        String configKey = "";
-                        int choice = 0;
-                        Matcher match = mConnectChoice.matcher(choiceStr);
-                        if (!match.find()) {
-                            if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
-                                    " Couldnt match pattern : " + choiceStr);
-                        } else {
-                            configKey = match.group(1);
-                            try {
-                                choice = Integer.parseInt(match.group(2));
-                            } catch (NumberFormatException e) {
-                                choice = 0;
+                    switch (key) {
+                        case SSID_KEY:
+                            if (config.isPasspoint()) {
+                                break;
                             }
-                            if (choice > 0) {
-                                if (config.connectChoices == null) {
-                                    config.connectChoices = new HashMap<String, Integer>();
+                            ssid = value;
+                            if (config.SSID != null && !config.SSID.equals(ssid)) {
+                                loge("Error parsing network history file, mismatched SSIDs");
+                                config = null; //error
+                                ssid = null;
+                            } else {
+                                config.SSID = ssid;
+                            }
+                            break;
+                        case FQDN_KEY:
+                            // Check for literal 'null' to be backwards compatible.
+                            config.FQDN = value.equals("null") ? null : value;
+                            break;
+                        case DEFAULT_GW_KEY:
+                            config.defaultGwMacAddress = value;
+                            break;
+                        case STATUS_KEY:
+                            config.autoJoinStatus = Integer.parseInt(value);
+                            break;
+                        case SUPPLICANT_DISABLE_REASON_KEY:
+                            config.disableReason = Integer.parseInt(value);
+                            break;
+                        case SELF_ADDED_KEY:
+                            config.selfAdded = Boolean.parseBoolean(value);
+                            break;
+                        case DID_SELF_ADD_KEY:
+                            config.didSelfAdd = Boolean.parseBoolean(value);
+                            break;
+                        case NO_INTERNET_ACCESS_REPORTS_KEY:
+                            config.numNoInternetAccessReports = Integer.parseInt(value);
+                            break;
+                        case VALIDATED_INTERNET_ACCESS_KEY:
+                            config.validatedInternetAccess = Boolean.parseBoolean(value);
+                            break;
+                        case CREATION_TIME_KEY:
+                            config.creationTime = value;
+                            break;
+                        case UPDATE_TIME_KEY:
+                            config.updateTime = value;
+                            break;
+                        case EPHEMERAL_KEY:
+                            config.ephemeral = Boolean.parseBoolean(value);
+                            break;
+                        case CREATOR_UID_KEY:
+                            config.creatorUid = Integer.parseInt(value);
+                            break;
+                        case BLACKLIST_MILLI_KEY:
+                            config.blackListTimestamp = Long.parseLong(value);
+                            break;
+                        case NUM_CONNECTION_FAILURES_KEY:
+                            config.numConnectionFailures = Integer.parseInt(value);
+                            break;
+                        case NUM_IP_CONFIG_FAILURES_KEY:
+                            config.numIpConfigFailures = Integer.parseInt(value);
+                            break;
+                        case NUM_AUTH_FAILURES_KEY:
+                            config.numIpConfigFailures = Integer.parseInt(value);
+                            break;
+                        case SCORER_OVERRIDE_KEY:
+                            config.numScorerOverride = Integer.parseInt(value);
+                            break;
+                        case SCORER_OVERRIDE_AND_SWITCH_KEY:
+                            config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value);
+                            break;
+                        case NUM_ASSOCIATION_KEY:
+                            config.numAssociation = Integer.parseInt(value);
+                            break;
+                        case JOIN_ATTEMPT_BOOST_KEY:
+                            config.autoJoinUseAggressiveJoinAttemptThreshold =
+                                    Integer.parseInt(value);
+                            break;
+                        case CONNECT_UID_KEY:
+                            config.lastConnectUid = Integer.parseInt(value);
+                            break;
+                        case UPDATE_UID_KEY:
+                            config.lastUpdateUid = Integer.parseInt(value);
+                            break;
+                        case FAILURE_KEY:
+                            config.lastFailure = value;
+                            break;
+                        case PEER_CONFIGURATION_KEY:
+                            config.peerWifiConfiguration = value;
+                            break;
+                        case CHOICE_KEY:
+                            String configKey = "";
+                            int choice = 0;
+                            Matcher match = mConnectChoice.matcher(value);
+                            if (!match.find()) {
+                                if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
+                                        " Couldnt match pattern : " + value);
+                            } else {
+                                configKey = match.group(1);
+                                try {
+                                    choice = Integer.parseInt(match.group(2));
+                                } catch (NumberFormatException e) {
+                                    choice = 0;
                                 }
-                                config.connectChoices.put(configKey, choice);
+                                if (choice > 0) {
+                                    if (config.connectChoices == null) {
+                                        config.connectChoices = new HashMap<>();
+                                    }
+                                    config.connectChoices.put(configKey, choice);
+                                }
                             }
-                        }
-                    }
-
-                    if (key.startsWith(LINK_KEY)) {
-                        String configKey = key.replace(LINK_KEY, "");
-                        configKey = configKey.replace(SEPARATOR_KEY, "");
-                        if (config.linkedConfigurations == null) {
-                            config.linkedConfigurations = new HashMap<String, Integer>();
-                        }
-                        if (config.linkedConfigurations != null) {
-                            config.linkedConfigurations.put(configKey, -1);
-                        }
-                    }
-
-                    if (key.startsWith(BSSID_KEY)) {
-                        if (key.startsWith(BSSID_KEY)) {
-                            bssid = key.replace(BSSID_KEY, "");
-                            bssid = bssid.replace(SEPARATOR_KEY, "");
+                            break;
+                        case LINK_KEY:
+                            if (config.linkedConfigurations == null) {
+                                config.linkedConfigurations = new HashMap<>();
+                            }
+                            else {
+                                config.linkedConfigurations.put(value, -1);
+                            }
+                            break;
+                        case BSSID_KEY:
+                            status = 0;
+                            ssid = null;
+                            bssid = null;
                             freq = 0;
                             seen = 0;
                             rssi = WifiConfiguration.INVALID_RSSI;
                             caps = "";
-                            status = 0;
-                        }
-
-                        if (key.startsWith(RSSI_KEY)) {
-                            String lvl = key.replace(RSSI_KEY, "");
-                            lvl = lvl.replace(SEPARATOR_KEY, "");
-                            rssi = Integer.parseInt(lvl);
-                        }
-
-                        if (key.startsWith(BSSID_STATUS_KEY)) {
-                            String st = key.replace(BSSID_STATUS_KEY, "");
-                            st = st.replace(SEPARATOR_KEY, "");
-                            status = Integer.parseInt(st);
-                        }
-
-                        if (key.startsWith(FREQ_KEY)) {
-                            String channel = key.replace(FREQ_KEY, "");
-                            channel = channel.replace(SEPARATOR_KEY, "");
-                            freq = Integer.parseInt(channel);
-                        }
-
-                        if (key.startsWith(DATE_KEY)) {
-                        /*
-                         * when reading the configuration from file we don't update the date
-                         * so as to avoid reading back stale or non-sensical data that would
-                         * depend on network time.
-                         * The date of a WifiConfiguration should only come from actual scan result.
-                         *
-                        String s = key.replace(FREQ_KEY, "");
-                        seen = Integer.getInteger(s);
-                        */
-                        }
-
-                        if (key.startsWith(BSSID_KEY_END)) {
+                            break;
+                        case RSSI_KEY:
+                            rssi = Integer.parseInt(value);
+                            break;
+                        case BSSID_STATUS_KEY:
+                            status = Integer.parseInt(value);
+                            break;
+                        case FREQ_KEY:
+                            freq = Integer.parseInt(value);
+                            break;
+                        case DATE_KEY:
+                            /*
+                             * when reading the configuration from file we don't update the date
+                             * so as to avoid reading back stale or non-sensical data that would
+                             * depend on network time.
+                             * The date of a WifiConfiguration should only come from actual scan result.
+                             *
+                            String s = key.replace(FREQ_KEY, "");
+                            seen = Integer.getInteger(s);
+                            */
+                            break;
+                        case BSSID_KEY_END:
                             if ((bssid != null) && (ssid != null)) {
 
-                                if (config.scanResultCache == null) {
-                                    config.scanResultCache = new HashMap<String, ScanResult>();
+                                if (getScanDetailCache(config) != null) {
+                                    WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
+                                    ScanDetail scanDetail = new ScanDetail(wssid, bssid,
+                                            caps, rssi, freq, (long) 0, seen);
+                                    getScanDetailCache(config).put(scanDetail);
+                                    scanDetail.getScanResult().autoJoinStatus = status;
                                 }
-                                WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
-                                ScanResult result = new ScanResult(wssid, bssid,
-                                        caps, rssi, freq, (long) 0);
-                                result.seen = seen;
-                                config.scanResultCache.put(bssid, result);
-                                result.autoJoinStatus = status;
                             }
-                        }
-
-                        if (key.startsWith(DELETED_CRC32_KEY)) {
-                            String crc = key.replace(DELETED_CRC32_KEY, "");
-                            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);
+                            break;
+                        case DELETED_CRC32_KEY:
+                            mDeletedSSIDs.add(Long.parseLong(value));
+                            break;
+                        case DELETED_EPHEMERAL_KEY:
+                            if (!TextUtils.isEmpty(value)) {
+                                mDeletedEphemeralSSIDs.add(value);
                             }
-                        }
+                            break;
+                        case CREATOR_NAME_KEY:
+                            config.creatorName = value;
+                            break;
+                        case UPDATE_NAME_KEY:
+                            config.lastUpdateName = value;
+                            break;
+                        case USER_APPROVED_KEY:
+                            config.userApproved = Integer.parseInt(value);
+                            break;
                     }
                 }
             }
-        } catch (EOFException ignore) {
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (Exception e) {
-                    loge("readNetworkHistory: Error reading file" + e);
-                }
-            }
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e);
+        } catch (EOFException e) {
+            // do nothing
         } catch (IOException e) {
-            loge("readNetworkHistory: No config file, revert to default" + e);
-        }
-
-        if(in!=null) {
-            try {
-                in.close();
-            } catch (Exception e) {
-                loge("readNetworkHistory: Error closing file" + e);
-            }
+            Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e);
         }
     }
 
     private void readAutoJoinConfig() {
-        BufferedReader reader = null;
-        try {
-
-            reader = new BufferedReader(new FileReader(autoJoinConfigFile));
-
+        try (BufferedReader reader = new BufferedReader(new FileReader(autoJoinConfigFile))) {
             for (String key = reader.readLine(); key != null; key = reader.readLine()) {
-                if (key != null) {
-                    Log.d(TAG, "readAutoJoinConfig line: " + key);
-                }
-                if (key.startsWith(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY)) {
-                    String st = key.replace(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableAutoJoinWhenAssociated = Integer.parseInt(st) != 0;
-                        Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhenAssociated);
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
+                Log.d(TAG, "readAutoJoinConfig line: " + key);
+
+                int split = key.indexOf(':');
+                if (split < 0) {
+                    continue;
                 }
 
-                if (key.startsWith(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY)) {
-                    String st = key.replace(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableFullBandScanWhenAssociated = Integer.parseInt(st) != 0;
-                        Log.d(TAG,"readAutoJoinConfig: enableFullBandScanWhenAssociated = "
-                                + enableFullBandScanWhenAssociated);
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
+                String name = key.substring(0, split);
+                Object reference = sKeyMap.get(name);
+                if (reference == null) {
+                    continue;
                 }
 
-                if (key.startsWith(ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY)) {
-                    String st = key.replace(ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableAutoJoinScanWhenAssociated = Integer.parseInt(st) != 0;
-                        Log.d(TAG,"readAutoJoinConfig: enabled = "
-                                + enableAutoJoinScanWhenAssociated);
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY)) {
-                    String st = key.replace(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableChipWakeUpWhenAssociated = Integer.parseInt(st) != 0;
-                        Log.d(TAG,"readAutoJoinConfig: enabled = "
-                                + enableChipWakeUpWhenAssociated);
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY)) {
-                    String st = key.replace(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableRssiPollWhenAssociated = Integer.parseInt(st) != 0;
-                        Log.d(TAG,"readAutoJoinConfig: enabled = "
-                                + enableRssiPollWhenAssociated);
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) {
-                    String st =
-                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = "
-                                + Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) {
-                    String st =
-                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = "
-                                + Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) {
-                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdUnblacklistThreshold5Hard = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = "
-                            + Integer.toString(thresholdUnblacklistThreshold5Hard));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) {
-                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdUnblacklistThreshold5Soft = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = "
-                            + Integer.toString(thresholdUnblacklistThreshold5Soft));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) {
-                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdUnblacklistThreshold24Hard = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = "
-                            + Integer.toString(thresholdUnblacklistThreshold24Hard));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) {
-                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdUnblacklistThreshold24Soft = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = "
-                            + Integer.toString(thresholdUnblacklistThreshold24Soft));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) {
-                    String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdGoodRssi5 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = "
-                            + Integer.toString(thresholdGoodRssi5));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) {
-                    String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdLowRssi5 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = "
-                            + Integer.toString(thresholdLowRssi5));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) {
-                    String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdBadRssi5 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = "
-                            + Integer.toString(thresholdBadRssi5));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) {
-                    String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdGoodRssi24 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = "
-                            + Integer.toString(thresholdGoodRssi24));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) {
-                    String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdLowRssi24 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = "
-                            + Integer.toString(thresholdLowRssi24));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) {
-                    String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        thresholdBadRssi24 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = "
-                            + Integer.toString(thresholdBadRssi24));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxTxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = "
-                            + Integer.toString(maxTxPacketForNetworkSwitching));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxRxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = "
-                            + Integer.toString(maxRxPacketForNetworkSwitching));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxTxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxTxPacketForFullScans = "
-                                + Integer.toString(maxTxPacketForFullScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxRxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxRxPacketForFullScans = "
-                                + Integer.toString(maxRxPacketForFullScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxTxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxTxPacketForPartialScans = "
-                                + Integer.toString(maxTxPacketForPartialScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY)) {
-                    String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxRxPacketForNetworkSwitching = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxRxPacketForPartialScans = "
-                                + Integer.toString(maxRxPacketForPartialScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-
-                if (key.startsWith(WIFI_VERBOSE_LOGS_KEY)) {
-                    String st = key.replace(WIFI_VERBOSE_LOGS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        enableVerboseLogging = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: enable verbose logs = "
-                                + Integer.toString(enableVerboseLogging));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
-                    String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        bandPreferenceBoostThreshold5 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: bandPreferenceBoostThreshold5 = "
-                            + Integer.toString(bandPreferenceBoostThreshold5));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY)) {
-                    String st = key.replace(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        associatedPartialScanPeriodMilli = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: associatedScanPeriod = "
-                                + Integer.toString(associatedPartialScanPeriodMilli));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(ASSOCIATED_FULL_SCAN_BACKOFF_KEY)) {
-                    String st = key.replace(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        associatedFullScanBackoff = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: associatedFullScanBackoff = "
-                                + Integer.toString(associatedFullScanBackoff));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
-                    String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        bandPreferencePenaltyThreshold5 = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: bandPreferencePenaltyThreshold5 = "
-                            + Integer.toString(bandPreferencePenaltyThreshold5));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY)) {
-                    String st = key.replace(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        alwaysEnableScansWhileAssociated = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: alwaysEnableScansWhileAssociated = "
-                                + Integer.toString(alwaysEnableScansWhileAssociated));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY)) {
-                    String st = key.replace(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxNumPassiveChannelsForPartialScans = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxNumPassiveChannelsForPartialScans = "
-                                + Integer.toString(maxNumPassiveChannelsForPartialScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-                if (key.startsWith(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY)) {
-                    String st = key.replace(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, "");
-                    st = st.replace(SEPARATOR_KEY, "");
-                    try {
-                        maxNumActiveChannelsForPartialScans = Integer.parseInt(st);
-                        Log.d(TAG,"readAutoJoinConfig: maxNumActiveChannelsForPartialScans = "
-                                + Integer.toString(maxNumActiveChannelsForPartialScans));
-                    } catch (NumberFormatException e) {
-                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
-                    }
-                }
-            }
-        } catch (EOFException ignore) {
-            if (reader != null) {
                 try {
-                    reader.close();
-                    reader = null;
-                } catch (Exception e) {
-                    loge("readAutoJoinStatus: Error closing file" + e);
+                    int value = Integer.parseInt(key.substring(split+1).trim());
+                    if (reference.getClass() == AtomicBoolean.class) {
+                        ((AtomicBoolean)reference).set(value != 0);
+                    }
+                    else {
+                        ((AtomicInteger)reference).set(value);
+                    }
+                    Log.d(TAG,"readAutoJoinConfig: " + name + " = " + value);
                 }
-            }
-        } catch (FileNotFoundException ignore) {
-            if (reader != null) {
-                try {
-                    reader.close();
-                    reader = null;
-                } catch (Exception e) {
-                    loge("readAutoJoinStatus: Error closing file" + e);
+                catch (NumberFormatException nfe) {
+                    Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
                 }
             }
         } catch (IOException e) {
             loge("readAutoJoinStatus: Error parsing configuration" + e);
         }
-
-        if (reader!=null) {
-           try {
-               reader.close();
-           } catch (Exception e) {
-               loge("readAutoJoinStatus: Error closing file" + e);
-           }
-        }
     }
 
 
@@ -2595,8 +2576,8 @@
 
         for (int i = 0; i < networks.size(); i++) {
             int id = networks.keyAt(i);
-            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
-
+            WifiConfiguration config = mConfiguredNetworks.getByConfigKeyID(id);
+            // This is the only place the map is looked up through a (dangerous) hash-value!
 
             if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED ||
                     config.ephemeral) {
@@ -2615,9 +2596,8 @@
      * and that can confuses the supplicant because it uses space charaters as delimiters
      */
 
-    private String encodeSSID(String str){
-        String tmp = removeDoubleQuotes(str);
-        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
+    public static String encodeSSID(String str){
+        return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8));
     }
 
     private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
@@ -2628,27 +2608,23 @@
          */
 
         if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
+        if (config.isPasspoint() && !mMOManager.isEnabled()) {
+            Log.e(TAG, "Passpoint is not enabled");
+            return new NetworkUpdateResult(INVALID_NETWORK_ID);
+        }
 
         int netId = config.networkId;
         boolean newNetwork = false;
         // networkId of INVALID_NETWORK_ID means we want to create a new network
         if (netId == INVALID_NETWORK_ID) {
-            Integer savedNetId = mNetworkIds.get(configKey(config));
-            // Check if either we have a network Id or a WifiConfiguration
-            // matching the one we are trying to add.
-            if (savedNetId == null) {
-                for (WifiConfiguration test : mConfiguredNetworks.values()) {
-                    if (test.configKey().equals(config.configKey())) {
-                        savedNetId = test.networkId;
-                        loge("addOrUpdateNetworkNative " + config.configKey()
-                                + " was found, but no network Id");
-                        break;
-                    }
-                }
-            }
-            if (savedNetId != null) {
-                netId = savedNetId;
+            WifiConfiguration savedConfig = mConfiguredNetworks.getByConfigKey(config.configKey());
+            if (savedConfig != null) {
+                netId = savedConfig.networkId;
             } else {
+                if (mMOManager.getHomeSP(config.FQDN) != null) {
+                    loge("addOrUpdateNetworkNative passpoint " + config.FQDN
+                            + " was found, but no network Id");
+                }
                 newNetwork = true;
                 netId = mWifiNative.addNetwork();
                 if (netId < 0) {
@@ -2673,8 +2649,18 @@
                 break setVariables;
             }
 
+            if (config.isPasspoint()) {
+                if (!mWifiNative.setNetworkVariable(
+                            netId,
+                            idStringVarName,
+                            '"' + config.FQDN + '"')) {
+                    loge("failed to set id_str: " + config.FQDN);
+                    break setVariables;
+                }
+            }
+
             if (config.BSSID != null) {
-                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
+                log("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
                 if (!mWifiNative.setNetworkVariable(
                         netId,
                         WifiConfiguration.bssidVarName,
@@ -2862,6 +2848,11 @@
                             // No need to try to set an obfuscated password, which will fail
                             continue;
                         }
+                        if (key.equals(WifiEnterpriseConfig.REALM_KEY)
+                                || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
+                            // No need to save realm or PLMN in supplicant
+                            continue;
+                        }
                         if (!mWifiNative.setNetworkVariable(
                                     netId,
                                     key,
@@ -2907,15 +2898,54 @@
                 currentConfig.lastConnectUid = config.lastConnectUid;
                 currentConfig.lastUpdateUid = config.lastUpdateUid;
                 currentConfig.creatorUid = config.creatorUid;
+                currentConfig.creatorName = config.creatorName;
+                currentConfig.lastUpdateName = config.lastUpdateName;
                 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
+                currentConfig.FQDN = config.FQDN;
+                currentConfig.providerFriendlyName = config.providerFriendlyName;
+                currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
+                currentConfig.validatedInternetAccess = config.validatedInternetAccess;
+                currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
+                currentConfig.updateTime = config.updateTime;
+                currentConfig.creationTime = config.creationTime;
             }
             if (DBG) {
-                loge("created new config netId=" + Integer.toString(netId)
-                        + " uid=" + Integer.toString(currentConfig.creatorUid));
+                log("created new config netId=" + Integer.toString(netId)
+                        + " uid=" + Integer.toString(currentConfig.creatorUid)
+                        + " name=" + currentConfig.creatorName);
             }
         }
 
-        if (uid >= 0) {
+        /* save HomeSP object for passpoint networks */
+        HomeSP homeSP = null;
+
+        if (config.isPasspoint()) {
+            try {
+                Credential credential =
+                        new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
+                HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
+                for (Long roamingConsortiumId : config.roamingConsortiumIds) {
+                    roamingConsortiumIds.add(roamingConsortiumId);
+                }
+
+                homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
+                        roamingConsortiumIds, Collections.<String>emptySet(),
+                        Collections.<Long>emptySet(), Collections.<Long>emptyList(),
+                        config.providerFriendlyName, null, credential);
+
+                log("created a homeSP object for " + config.networkId + ":" + config.SSID);
+
+                /* fix enterprise config properties for passpoint */
+                currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
+                currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
+            }
+            catch (IOException ioe) {
+                Log.e(TAG, "Failed to create Passpoint config: " + ioe);
+                return new NetworkUpdateResult(INVALID_NETWORK_ID);
+            }
+        }
+
+        if (uid != WifiConfiguration.UNKNOWN_UID) {
             if (newNetwork) {
                 currentConfig.creatorUid = uid;
             } else {
@@ -2923,8 +2953,18 @@
             }
         }
 
+        // For debug, record the time the configuration was modified
+        StringBuilder sb = new StringBuilder();
+        sb.append("time=");
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(System.currentTimeMillis());
+        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
+
         if (newNetwork) {
             currentConfig.dirty = true;
+            currentConfig.creationTime = sb.toString();
+        } else {
+            currentConfig.updateTime = sb.toString();
         }
 
         if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
@@ -2934,7 +2974,7 @@
             currentConfig.selfAdded = false;
             currentConfig.didSelfAdd = false;
             if (DBG) {
-                loge("remove deleted status netId=" + Integer.toString(netId)
+                log("remove deleted status netId=" + Integer.toString(netId)
                         + " " + currentConfig.configKey());
             }
         }
@@ -2948,26 +2988,59 @@
                 currentConfig.ephemeral) {
             // Make the config non-ephemeral since the user just explicitly clicked it.
             currentConfig.ephemeral = false;
-            if (DBG) loge("remove ephemeral status netId=" + Integer.toString(netId)
+            if (DBG) log("remove ephemeral status netId=" + Integer.toString(netId)
                     + " " + currentConfig.configKey());
         }
 
-        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
+        if (VDBG) log("will read network variables netId=" + Integer.toString(netId));
 
         readNetworkVariables(currentConfig);
 
+        // Persist configuration paramaters that are not saved by supplicant.
+        if (config.lastUpdateName != null) {
+            currentConfig.lastUpdateName = config.lastUpdateName;
+        }
+        if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
+            currentConfig.lastUpdateUid = config.lastUpdateUid;
+        }
+
         mConfiguredNetworks.put(netId, currentConfig);
-        mNetworkIds.put(configKey(currentConfig), netId);
 
         NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
         result.setIsNewNetwork(newNetwork);
         result.setNetworkId(netId);
 
+        if (homeSP != null) {
+            writePasspointConfigs(null, homeSP);
+        }
         writeKnownNetworkHistory(false);
 
         return result;
     }
 
+    public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
+        WifiConfiguration config = mConfiguredNetworks.getByFQDN(homeSP.getFQDN());
+        if (config == null) {
+            Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
+        }
+        return config;
+    }
+
+    private HomeSP getHomeSPForConfig(WifiConfiguration config) {
+        WifiConfiguration storedConfig = mConfiguredNetworks.get(config.networkId);
+        return storedConfig != null && storedConfig.isPasspoint() ?
+                mMOManager.getHomeSP(storedConfig.FQDN) : null;
+    }
+
+    public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
+        if (config == null) return null;
+        ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
+        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+            cache = new ScanDetailCache(config);
+            mScanDetailCaches.put(config.networkId, cache);
+        }
+        return cache;
+    }
 
     /**
      * This function run thru the Saved WifiConfigurations and check if some should be linked.
@@ -2975,7 +3048,7 @@
      */
     public void linkConfiguration(WifiConfiguration config) {
 
-        if (config.scanResultCache != null && config.scanResultCache.size() > 6) {
+        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
             // Ignore configurations with large number of BSSIDs
             return;
         }
@@ -3000,7 +3073,8 @@
                 continue;
             }
 
-            if (link.scanResultCache != null && link.scanResultCache.size() > 6) {
+            ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
+            if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
                 // Ignore configurations with large number of BSSIDs
                 continue;
             }
@@ -3019,10 +3093,11 @@
                 // hoping that WifiConfigurations are indeed behind the same gateway.
                 // once both WifiConfiguration have been tried and thus once both efault gateways
                 // are known we will revisit the choice of linking them
-                if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
-                        && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
-                    for (String abssid : config.scanResultCache.keySet()) {
-                        for (String bbssid : link.scanResultCache.keySet()) {
+                if ((getScanDetailCache(config) != null)
+                        && (getScanDetailCache(config).size() <= 6)) {
+
+                    for (String abssid : getScanDetailCache(config).keySet()) {
+                        for (String bbssid : linkedScanDetailCache.keySet()) {
                             if (VVDBG) {
                                 loge("linkConfiguration try to link due to DBDC BSSID match "
                                         + link.SSID +
@@ -3052,8 +3127,8 @@
 
             if (doLink) {
                 if (VDBG) {
-                   loge("linkConfiguration: will link " + link.configKey()
-                           + " and " + config.configKey());
+                    loge("linkConfiguration: will link " + link.configKey()
+                            + " and " + config.configKey());
                 }
                 if (link.linkedConfigurations == null) {
                     link.linkedConfigurations = new HashMap<String, Integer>();
@@ -3092,139 +3167,6 @@
         }
     }
 
-    /*
-     * We try to link a scan result with a WifiConfiguration for which SSID and
-     * key management dont match,
-     * for instance, we try identify the 5GHz SSID of a DBDC AP,
-     * even though we know only of the 2.4GHz
-     *
-     * Obviously, this function is not optimal since it is used to compare every scan
-     * result with every Saved WifiConfiguration, with a string.equals operation.
-     * As a speed up, might be better to implement the mConfiguredNetworks store as a
-     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
-     * so as to speed this up. Also to prevent the tiny probability of hash collision.
-     *
-     */
-    public WifiConfiguration associateWithConfiguration(ScanResult result) {
-        boolean doNotAdd = false;
-        String configKey = WifiConfiguration.configKey(result);
-        if (configKey == null) {
-            if (DBG) loge("associateWithConfiguration(): no config key " );
-            return null;
-        }
-
-        // Need to compare with quoted string
-        String SSID = "\"" + result.SSID + "\"";
-
-        if (VVDBG) {
-            loge("associateWithConfiguration(): try " + configKey);
-        }
-
-        Checksum csum = new CRC32();
-        csum.update(SSID.getBytes(), 0, SSID.getBytes().length);
-        if (mDeletedSSIDs.contains(csum.getValue())) {
-            doNotAdd = true;
-        }
-
-        WifiConfiguration config = null;
-        for (WifiConfiguration link : mConfiguredNetworks.values()) {
-            boolean doLink = false;
-
-            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.selfAdded ||
-                    link.ephemeral) {
-                if (VVDBG) loge("associateWithConfiguration(): skip selfadd " + link.configKey() );
-                // Make sure we dont associate the scan result to a deleted config
-                continue;
-            }
-
-            if (!link.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
-                if (VVDBG) loge("associateWithConfiguration(): skip non-PSK " + link.configKey() );
-                // Make sure we dont associate the scan result to a non-PSK config
-                continue;
-            }
-
-            if (configKey.equals(link.configKey())) {
-                if (VVDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
-                return link; // Found it exactly
-            }
-
-            if (!doNotAdd && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
-                for (String bssid : link.scanResultCache.keySet()) {
-                    if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
-                            && SSID.regionMatches(false, 0, link.SSID, 0, 4)) {
-                        // If first 16 ascii characters of BSSID matches, and first 3
-                        // characters of SSID match, we assume this is a home setup
-                        // and thus we will try to transfer the password from the known
-                        // BSSID/SSID to the recently found BSSID/SSID
-
-                        // If (VDBG)
-                        //    loge("associateWithConfiguration OK " );
-                        doLink = true;
-                        break;
-                    }
-                }
-            }
-
-            if (doLink) {
-                // Try to make a non verified WifiConfiguration, but only if the original
-                // configuration was not self already added
-                if (VDBG) {
-                    loge("associateWithConfiguration: try to create " +
-                            result.SSID + " and associate it with: " + link.SSID
-                            + " key " + link.configKey());
-                }
-                config = wifiConfigurationFromScanResult(result);
-                if (config != null) {
-                    config.selfAdded = true;
-                    config.didSelfAdd = true;
-                    config.dirty = true;
-                    config.peerWifiConfiguration = link.configKey();
-                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
-                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
-                        if (VDBG && config != null) {
-                            loge("associateWithConfiguration: got a config from beacon"
-                                    + config.SSID + " key " + config.configKey());
-                        }
-                        // Transfer the credentials from the configuration we are linking from
-                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
-                        if (psk != null) {
-                            config.preSharedKey = psk;
-                            if (VDBG) {
-                                if (config.preSharedKey != null)
-                                    loge(" transfer PSK : " + config.preSharedKey);
-                            }
-
-                            // Link configurations
-                            if (link.linkedConfigurations == null) {
-                                link.linkedConfigurations = new HashMap<String, Integer>();
-                            }
-                            if (config.linkedConfigurations == null) {
-                                config.linkedConfigurations = new HashMap<String, Integer>();
-                            }
-                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
-                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
-
-                            // Carry over the Ip configuration
-                            if (link.getIpConfiguration() != null) {
-                                config.setIpConfiguration(link.getIpConfiguration());
-                            }
-                        } else {
-                            config = null;
-                        }
-                    } else {
-                        config = null;
-                    }
-                    if (config != null) break;
-                }
-                if (VDBG && config != null) {
-                    loge("associateWithConfiguration: success, created: " + config.SSID
-                            + " key " + config.configKey());
-                }
-            }
-        }
-        return config;
-    }
-
     public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
         if (config == null)
             return null;
@@ -3233,7 +3175,7 @@
         HashSet<Integer> channels = new HashSet<Integer>();
 
         //get channels for this configuration, if there are at least 2 BSSIDs
-        if (config.scanResultCache == null && config.linkedConfigurations == null) {
+        if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
             return null;
         }
 
@@ -3242,8 +3184,8 @@
             dbg.append("makeChannelList age=" + Integer.toString(age)
                     + " for " + config.configKey()
                     + " max=" + maxNumActiveChannelsForPartialScans);
-            if (config.scanResultCache != null) {
-                dbg.append(" bssids=" + config.scanResultCache.size());
+            if (getScanDetailCache(config) != null) {
+                dbg.append(" bssids=" + getScanDetailCache(config).size());
             }
             if (config.linkedConfigurations != null) {
                 dbg.append(" linked=" + config.linkedConfigurations.size());
@@ -3252,10 +3194,11 @@
         }
 
         int numChannels = 0;
-        if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
-            for (ScanResult result : config.scanResultCache.values()) {
+        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
+            for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+                ScanResult result = scanDetail.getScanResult();
                 //TODO : cout active and passive channels separately
-                if (numChannels > maxNumActiveChannelsForPartialScans) {
+                if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
                     break;
                 }
                 if (VDBG) {
@@ -3276,16 +3219,17 @@
                 WifiConfiguration linked = getWifiConfiguration(key);
                 if (linked == null)
                     continue;
-                if (linked.scanResultCache == null) {
+                if (getScanDetailCache(linked) == null) {
                     continue;
                 }
-                for (ScanResult result : linked.scanResultCache.values()) {
+                for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
+                    ScanResult result = scanDetail.getScanResult();
                     if (VDBG) {
                         loge("has link: " + result.BSSID
                                 + " freq=" + Integer.toString(result.frequency)
                                 + " age=" + Long.toString(now_ms - result.seen));
                     }
-                    if (numChannels > maxNumActiveChannelsForPartialScans) {
+                    if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
                         break;
                     }
                     if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
@@ -3298,15 +3242,214 @@
         return channels;
     }
 
+    private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
+        if (!mMOManager.isConfigured()) {
+            return null;
+        }
+        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+        if (!networkDetail.hasInterworking()) {
+            return null;
+        }
+        updateAnqpCache(scanDetail, networkDetail.getANQPElements());
+
+        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
+        Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
+                " pass 1 matches: " + toMatchString(matches));
+        return matches;
+    }
+
+    private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
+        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
+        ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
+
+        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
+                anqpData != null ? anqpData.getANQPElements() : null;
+
+        boolean queried = !query;
+        Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
+        Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
+        Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() +
+                ", anqp " + ( anqpData != null ? "present" : "missing" ) +
+                ", query " + query + ", home sps: " + homeSPs.size());
+
+        for (HomeSP homeSP : homeSPs) {
+            PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
+
+            Log.d(Utils.hs2LogTag(getClass()), " -- " +
+                    homeSP.getFQDN() + ": match " + match + ", queried " + queried);
+
+            if (match == PasspointMatch.Incomplete && !queried) {
+                if (mAnqpCache.initiate(networkDetail)) {
+                    mSupplicantBridge.startANQP(scanDetail);
+                }
+                queried = true;
+            }
+            matches.put(homeSP, match);
+        }
+        return matches;
+    }
+
+    public void notifyANQPDone(Long bssid, boolean success) {
+        mSupplicantBridge.notifyANQPDone(bssid, success);
+    }
+
+    public void notifyANQPResponse(ScanDetail scanDetail,
+                                   Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+
+        updateAnqpCache(scanDetail, anqpElements);
+        if (anqpElements == null || anqpElements.isEmpty()) {
+            return;
+        }
+        scanDetail.propagateANQPInfo(anqpElements);
+
+        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
+        Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
+                " pass 2 matches: " + toMatchString(matches));
+
+        cacheScanResultForPasspointConfigs(scanDetail, matches);
+    }
+
+
+    private void updateAnqpCache(ScanDetail scanDetail,
+                                 Map<Constants.ANQPElementType,ANQPElement> anqpElements)
+    {
+        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
+        if (anqpElements == null) {
+            // Try to pull cached data if query failed.
+            ANQPData data = mAnqpCache.getEntry(networkDetail);
+            if (data != null) {
+                scanDetail.propagateANQPInfo(data.getANQPElements());
+            }
+            return;
+        }
+
+        mAnqpCache.update(networkDetail, anqpElements);
+    }
+
+    private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
+            sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
+        }
+        return sb.toString();
+    }
+
+    private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
+                                           Map<HomeSP,PasspointMatch> matches) {
+
+        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
+            PasspointMatch match = entry.getValue();
+            if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
+                WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
+                if (config != null) {
+                    cacheScanResultForConfig(config, scanDetail, entry.getValue());
+                } else {
+		            Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" +
+                            entry.getKey().getFQDN() + "'");
+                    /* perhaps the configuration was deleted?? */
+                }
+            }
+        }
+    }
+
+    private void cacheScanResultForConfig(
+            WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
+
+        ScanResult scanResult = scanDetail.getScanResult();
+
+        if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
+            if (VVDBG) {
+                loge("updateSavedNetworkHistory(): found a deleted, skip it...  "
+                        + config.configKey());
+            }
+            // The scan result belongs to a deleted config:
+            //   - increment numConfigFound to remember that we found a config
+            //            matching for this scan result
+            //   - dont do anything since the config was deleted, just skip...
+            return;
+        }
+
+        ScanDetailCache scanDetailCache = getScanDetailCache(config);
+        if (scanDetailCache == null) {
+            Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
+            return;
+        }
+
+        // Adding a new BSSID
+        ScanResult result = scanDetailCache.get(scanResult.BSSID);
+        if (result != null) {
+            // transfer the black list status
+            scanResult.autoJoinStatus = result.autoJoinStatus;
+            scanResult.blackListTimestamp = result.blackListTimestamp;
+            scanResult.numIpConfigFailures = result.numIpConfigFailures;
+            scanResult.numConnection = result.numConnection;
+            scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
+        }
+
+        if (config.ephemeral) {
+            // For an ephemeral Wi-Fi config, the ScanResult should be considered
+            // untrusted.
+            scanResult.untrusted = true;
+        }
+
+        if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) {
+            long now_dbg = 0;
+            if (VVDBG) {
+                loge(" Will trim config " + config.configKey()
+                        + " size " + scanDetailCache.size());
+
+                for (ScanDetail sd : scanDetailCache.values()) {
+                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
+                }
+                now_dbg = SystemClock.elapsedRealtimeNanos();
+            }
+            // Trim the scan result cache to maxNumScanCacheEntries entries max
+            // Since this operation is expensive, make sure it is not performed
+            // until the cache has grown significantly above the trim treshold
+            scanDetailCache.trim(maxNumScanCacheEntries);
+            if (VVDBG) {
+                long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
+                loge(" Finished trimming config, time(ns) " + diff);
+                for (ScanDetail sd : scanDetailCache.values()) {
+                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
+                }
+            }
+        }
+
+        // Add the scan result to this WifiConfiguration
+        if (passpointMatch != null)
+            scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
+        else
+            scanDetailCache.put(scanDetail);
+
+        // Since we added a scan result to this configuration, re-attempt linking
+        linkConfiguration(config);
+    }
+
+
     // Update the WifiConfiguration database with the new scan result
     // A scan result can be associated to multiple WifiConfigurations
-    public boolean updateSavedNetworkHistory(ScanResult scanResult) {
+    public boolean updateSavedNetworkHistory(ScanDetail scanDetail) {
+
+        ScanResult scanResult = scanDetail.getScanResult();
+        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
         int numConfigFound = 0;
         if (scanResult == null)
             return false;
 
         String SSID = "\"" + scanResult.SSID + "\"";
 
+        if (networkDetail.hasInterworking()) {
+            Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
+            if (matches != null) {
+                cacheScanResultForPasspointConfigs(scanDetail, matches);
+                return matches.size() != 0;
+            }
+        }
+
         for (WifiConfiguration config : mConfiguredNetworks.values()) {
             boolean found = false;
 
@@ -3344,70 +3487,7 @@
 
             if (found) {
                 numConfigFound ++;
-
-                if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
-                    if (VVDBG) {
-                        loge("updateSavedNetworkHistory(): found a deleted, skip it...  "
-                                + config.configKey());
-                    }
-                    // The scan result belongs to a deleted config:
-                    //   - increment numConfigFound to remember that we found a config
-                    //            matching for this scan result
-                    //   - dont do anything since the config was deleted, just skip...
-                    continue;
-                }
-
-                if (config.scanResultCache == null) {
-                    config.scanResultCache = new HashMap<String, ScanResult>();
-                }
-
-                // Adding a new BSSID
-                ScanResult result = config.scanResultCache.get(scanResult.BSSID);
-                if (result == null) {
-                    config.dirty = true;
-                } else {
-                    // transfer the black list status
-                    scanResult.autoJoinStatus = result.autoJoinStatus;
-                    scanResult.blackListTimestamp = result.blackListTimestamp;
-                    scanResult.numIpConfigFailures = result.numIpConfigFailures;
-                    scanResult.numConnection = result.numConnection;
-                    scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
-                }
-
-                if (config.ephemeral) {
-                    // For an ephemeral Wi-Fi config, the ScanResult should be considered
-                    // untrusted.
-                    scanResult.untrusted = true;
-                }
-
-                if (config.scanResultCache.size() > (maxNumScanCacheEntries + 64)) {
-                    long now_dbg = 0;
-                    if (VVDBG) {
-                        loge(" Will trim config " + config.configKey()
-                                + " size " + config.scanResultCache.size());
-
-                        for (ScanResult r : config.scanResultCache.values()) {
-                            loge("     " + result.BSSID + " " + result.seen);
-                        }
-                        now_dbg = SystemClock.elapsedRealtimeNanos();
-                    }
-                    // Trim the scan result cache to maxNumScanCacheEntries entries max
-                    // Since this operation is expensive, make sure it is not performed
-                    // until the cache has grown significantly above the trim treshold
-                    config.trimScanResultsCache(maxNumScanCacheEntries);
-                    if (VVDBG) {
-                        long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
-                        loge(" Finished trimming config, time(ns) " + diff);
-                        for (ScanResult r : config.scanResultCache.values()) {
-                            loge("     " + r.BSSID + " " + r.seen);
-                        }
-                    }
-                }
-
-                // Add the scan result to this WifiConfiguration
-                config.scanResultCache.put(scanResult.BSSID, scanResult);
-                // Since we added a scan result to this configuration, re-attempt linking
-                linkConfiguration(config);
+                cacheScanResultForConfig(config, scanDetail, null);
             }
 
             if (VDBG && found) {
@@ -3418,7 +3498,7 @@
                 loge("        got known scan result " +
                         scanResult.BSSID + " key : "
                         + config.configKey() + " num: " +
-                        Integer.toString(config.scanResultCache.size())
+                        Integer.toString(getScanDetailCache(config).size())
                         + " rssi=" + Integer.toString(scanResult.level)
                         + " freq=" + Integer.toString(scanResult.frequency)
                         + status);
@@ -3609,70 +3689,20 @@
             config.preSharedKey = null;
         }
 
-        value = mWifiNative.getNetworkVariable(config.networkId,
-                WifiConfiguration.Protocol.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.Protocol.strings);
-                if (0 <= index) {
-                    config.allowedProtocols.set(index);
-                }
-            }
-        }
+        readNetworkBitsetVariable(config.networkId, config.allowedProtocols,
+                WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings);
 
-        value = mWifiNative.getNetworkVariable(config.networkId,
-                WifiConfiguration.KeyMgmt.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
-                if (0 <= index) {
-                    config.allowedKeyManagement.set(index);
-                }
-            }
-        }
+        readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement,
+                WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings);
 
-        value = mWifiNative.getNetworkVariable(config.networkId,
-                WifiConfiguration.AuthAlgorithm.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
-                if (0 <= index) {
-                    config.allowedAuthAlgorithms.set(index);
-                }
-            }
-        }
+        readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms,
+                WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings);
 
-        value = mWifiNative.getNetworkVariable(config.networkId,
-                WifiConfiguration.PairwiseCipher.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
-                if (0 <= index) {
-                    config.allowedPairwiseCiphers.set(index);
-                }
-            }
-        }
+        readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers,
+                WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings);
 
-        value = mWifiNative.getNetworkVariable(config.networkId,
-                WifiConfiguration.GroupCipher.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.GroupCipher.strings);
-                if (0 <= index) {
-                    config.allowedGroupCiphers.set(index);
-                }
-            }
-        }
+        readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers,
+                WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings);
 
         if (config.enterpriseConfig == null) {
             config.enterpriseConfig = new WifiEnterpriseConfig();
@@ -3746,7 +3776,9 @@
 
     /* return the allowed key management based on a scan result */
 
-    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
+    public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
+
+        ScanResult result = scanDetail.getScanResult();
         WifiConfiguration config = new WifiConfiguration();
 
         config.SSID = "\"" + result.SSID + "\"";
@@ -3771,10 +3803,7 @@
             config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
         }
 
-        config.scanResultCache = new HashMap<String, ScanResult>();
-        if (config.scanResultCache == null)
-            return null;
-        config.scanResultCache.put(result.BSSID, result);
+        /* getScanDetailCache(config).put(scanDetail); */
 
         return config;
     }
@@ -3790,16 +3819,26 @@
         pw.println("Dump of WifiConfigStore");
         pw.println("mLastPriority " + mLastPriority);
         pw.println("Configured networks");
-        for (WifiConfiguration conf : getConfiguredNetworks()) {
+        for (WifiConfiguration conf : getAllConfiguredNetworks()) {
             pw.println(conf);
         }
         pw.println();
-
+        if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
+            pw.println("LostConfigs: ");
+            for (String s : mLostConfigsDbg) {
+                pw.println(s);
+            }
+        }
         if (mLocalLog != null) {
             pw.println("WifiConfigStore - Log Begin ----");
             mLocalLog.dump(fd, pw, args);
             pw.println("WifiConfigStore - Log End ----");
         }
+        if (mMOManager.isConfigured()) {
+            pw.println("Begin dump of ANQP Cache");
+            mAnqpCache.dump(pw);
+            pw.println("End dump of ANQP Cache");
+        }
     }
 
     public String getConfigFile() {
@@ -3821,6 +3860,14 @@
         }
     }
 
+    private void logKernelTime() {
+        long kernelTimeMs = System.nanoTime()/(1000*1000);
+        StringBuilder builder = new StringBuilder();
+        builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
+                (kernelTimeMs%1000).append("\n");
+        localLog(builder.toString());
+    }
+
     protected void log(String s) {
         Log.d(TAG, s);
     }
@@ -3842,7 +3889,7 @@
         }
 
         WifiConfiguration config;
-        synchronized(mConfiguredNetworks) {
+        synchronized(mConfiguredNetworks) {             // !!! Useless synchronization
             config = mConfiguredNetworks.get(netId);
         }
 
@@ -3928,6 +3975,112 @@
         return false;
     }
 
+    boolean isNetworkConfigured(WifiConfiguration config) {
+        // Check if either we have a network Id or a WifiConfiguration
+        // matching the one we are trying to add.
+
+        if(config.networkId != INVALID_NETWORK_ID) {
+            return (mConfiguredNetworks.get(config.networkId) != null);
+        }
+
+        return (mConfiguredNetworks.getByConfigKey(config.configKey()) != null);
+    }
+
+    /**
+     * Checks if uid has access to modify the configuration corresponding to networkId.
+     *
+     * Factors involved in modifiability of a config are as follows.
+     *    If uid is a Device Owner app then it has full control over the device, including WiFi
+     * configs.
+     *    If the modification is only for administrative annotation (e.g. when connecting) or the
+     * config is not lockdown eligible (currently that means any config not last updated by the DO)
+     * then the creator of config or an app holding OVERRIDE_CONFIG_WIFI can modify the config.
+     *    If the config is lockdown eligible and the modification is substantial (not annotation)
+     * then the requirement to be able to modify the config by the uid is as follows:
+     *    a) the uid has to hold OVERRIDE_CONFIG_WIFI and
+     *    b) the lockdown feature should be disabled.
+     */
+    boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
+        WifiConfiguration config = mConfiguredNetworks.get(networkId);
+
+        if (config == null) {
+            loge("canModifyNetwork: cannot find config networkId " + networkId);
+            return false;
+        }
+
+        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+                DevicePolicyManagerInternal.class);
+
+        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
+                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+        if (isUidDeviceOwner) {
+            // Device Owner has full control over the device, including WiFi Configs
+            return true;
+        }
+
+        final boolean isCreator = (config.creatorUid == uid);
+
+        if (onlyAnnotate) {
+            return isCreator || checkConfigOverridePermission(uid);
+        }
+
+        // Check if device has DPM capability. If it has and dpmi is still null, then we
+        // treat this case with suspicion and bail out.
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+                && dpmi == null) {
+            return false;
+        }
+
+        // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
+
+        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
+                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        if (!isConfigEligibleForLockdown) {
+            return isCreator || checkConfigOverridePermission(uid);
+        }
+
+        final ContentResolver resolver = mContext.getContentResolver();
+        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
+                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
+        return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
+    }
+
+    /**
+     * Checks if uid has access to modify config.
+     */
+    boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
+        if (config == null) {
+            loge("canModifyNetowrk recieved null configuration");
+            return false;
+        }
+
+        // Resolve the correct network id.
+        int netid;
+        if (config.networkId != INVALID_NETWORK_ID){
+            netid = config.networkId;
+        } else {
+            WifiConfiguration test = mConfiguredNetworks.getByConfigKey(config.configKey());
+            if (test == null) {
+                return false;
+            } else {
+                netid = test.networkId;
+            }
+        }
+
+        return canModifyNetwork(uid, netid, onlyAnnotate);
+    }
+
+    boolean checkConfigOverridePermission(int uid) {
+        try {
+            return (AppGlobals.getPackageManager().checkUidPermission(
+                    android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
+                    == PackageManager.PERMISSION_GRANTED);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     /** called when CS ask WiFistateMachine to disconnect the current network
      * because the score is bad.
      */
@@ -3962,16 +4115,17 @@
 
         // Look for the BSSID in our config store
         for (WifiConfiguration config : mConfiguredNetworks.values()) {
-            if (config.scanResultCache != null) {
-                for (ScanResult result: config.scanResultCache.values()) {
-                    if (result.BSSID.equals(BSSID)) {
+            if (getScanDetailCache(config) != null) {
+                for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+                    if (scanDetail.getBSSIDString().equals(BSSID)) {
                         if (enable) {
-                            result.setAutoJoinStatus(ScanResult.ENABLED);
+                            scanDetail.getScanResult().setAutoJoinStatus(ScanResult.ENABLED);
                         } else {
                             // Black list the BSSID we were trying to join
                             // so as the Roam state machine
                             // doesn't pick it up over and over
-                            result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED);
+                            scanDetail.getScanResult().setAutoJoinStatus(
+                                    ScanResult.AUTO_ROAM_DISABLED);
                             found = true;
                         }
                     }
@@ -3987,20 +4141,48 @@
                 DEFAULT_MAX_DHCP_RETRIES);
     }
 
+    void clearBssidBlacklist() {
+        if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
+            if(DBG) {
+                Log.d(TAG, "No blacklist allowed without epno enabled");
+            }
+            return;
+        }
+        mBssidBlacklist = new HashSet<String>();
+        mWifiNative.clearBlacklist();
+        mWifiNative.setBssidBlacklist(null);
+    }
+
+    void blackListBssid(String BSSID) {
+        if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
+            if(DBG) {
+                Log.d(TAG, "No blacklist allowed without epno enabled");
+            }
+            return;
+        }
+        if (BSSID == null)
+            return;
+        mBssidBlacklist.add(BSSID);
+        // Blacklist at wpa_supplicant
+        mWifiNative.addToBlacklist(BSSID);
+        // Blacklist at firmware
+        String list[] = new String[mBssidBlacklist.size()];
+        int count = 0;
+        for (String bssid : mBssidBlacklist) {
+            list[count++] = bssid;
+        }
+        mWifiNative.setBssidBlacklist(list);
+    }
+
     void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
         if (config != null) {
             if (enabled) {
-                loge("SSID re-enabled for  " + config.configKey() +
+                loge("Ignoring SSID re-enabled from supplicant:  " + config.configKey() +
                         " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
                         + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
-                //TODO: http://b/16381983 Fix Wifi Network Blacklisting
-                //TODO: really I don't know if re-enabling is right but we
-                //TODO: should err on the side of trying to connect
-                //TODO: even if the attempt will fail
-                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
-                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
-                }
+                //We should not re-enable the BSSID based on Supplicant reanable.
+                // Framework will re-enable it after its own blacklist timer expires
             } else {
                 loge("SSID temp disabled for  " + config.configKey() +
                         " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
@@ -4059,8 +4241,8 @@
                             // Also blacklist the BSSId if we find it
                             ScanResult result = null;
                             String bssidDbg = "";
-                            if (config.scanResultCache != null && BSSID != null) {
-                                result = config.scanResultCache.get(BSSID);
+                            if (getScanDetailCache(config) != null && BSSID != null) {
+                                result = getScanDetailCache(config).get(BSSID);
                             }
                             if (result != null) {
                                 result.numIpConfigFailures ++;
@@ -4130,7 +4312,7 @@
             ret = putCertInKeyStore(userCertName, config.getClientCertificate());
             if (ret == false) {
                 // Remove private key installed
-                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
+                mKeyStore.delete(privKeyName, Process.WIFI_UID);
                 return ret;
             }
         }
@@ -4140,7 +4322,7 @@
             if (ret == false) {
                 if (config.getClientCertificate() != null) {
                     // Remove client key+cert
-                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
+                    mKeyStore.delete(privKeyName, Process.WIFI_UID);
                     mKeyStore.delete(userCertName, Process.WIFI_UID);
                 }
                 return ret;
@@ -4179,7 +4361,7 @@
         // a valid client certificate is configured
         if (!TextUtils.isEmpty(client)) {
             if (DBG) Log.d(TAG, "removing client private key and user cert");
-            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+            mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
             mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
         }
 
@@ -4269,4 +4451,18 @@
         }
     }
 
+    private void readNetworkBitsetVariable(int netId, BitSet variable, String varName,
+            String[] strings) {
+        String value = mWifiNative.getNetworkVariable(netId, varName);
+        if (!TextUtils.isEmpty(value)) {
+            variable.clear();
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index = lookupString(val, strings);
+                if (0 <= index) {
+                    variable.set(index);
+                }
+            }
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java
index 8f36cb7..3b41e3b 100644
--- a/service/java/com/android/server/wifi/WifiController.java
+++ b/service/java/com/android/server/wifi/WifiController.java
@@ -114,6 +114,7 @@
     static final int CMD_SET_AP                     = BASE + 10;
     static final int CMD_DEFERRED_TOGGLE            = BASE + 11;
     static final int CMD_USER_PRESENT               = BASE + 12;
+    static final int CMD_AP_START_FAILURE           = BASE + 13;
 
     private DefaultState mDefaultState = new DefaultState();
     private StaEnabledState mStaEnabledState = new StaEnabledState();
@@ -172,6 +173,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_DEVICE_IDLE);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
@@ -182,6 +184,14 @@
                         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
                                     WifiManager.EXTRA_NETWORK_INFO);
+                        } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+                            int state = intent.getIntExtra(
+                                    WifiManager.EXTRA_WIFI_AP_STATE,
+                                    WifiManager.WIFI_AP_STATE_FAILED);
+                            if(state == WifiManager.WIFI_AP_STATE_FAILED) {
+                                loge(TAG + "SoftAP start failed");
+                                sendMessage(CMD_AP_START_FAILURE);
+                            }
                         }
                     }
                 },
@@ -371,6 +381,7 @@
                 case CMD_WIFI_TOGGLED:
                 case CMD_AIRPLANE_TOGGLED:
                 case CMD_EMERGENCY_MODE_CHANGED:
+                case CMD_AP_START_FAILURE:
                     break;
                 case CMD_USER_PRESENT:
                     mFirstUserSignOnSeen = true;
@@ -398,6 +409,7 @@
             mDisabledTimestamp = SystemClock.elapsedRealtime();
             mDeferredEnableSerialNumber++;
             mHaveDeferredEnable = false;
+            mWifiStateMachine.clearANQPCache();
         }
         @Override
         public boolean processMessage(Message msg) {
@@ -519,6 +531,7 @@
             mDisabledTimestamp = SystemClock.elapsedRealtime();
             mDeferredEnableSerialNumber++;
             mHaveDeferredEnable = false;
+            mWifiStateMachine.clearANQPCache();
         }
 
         @Override
@@ -607,6 +620,12 @@
                         transitionTo(mApStaDisabledState);
                     }
                     break;
+                case CMD_AP_START_FAILURE:
+                    if(!mSettingsStore.isScanAlwaysAvailable()) {
+                        transitionTo(mApStaDisabledState);
+                    } else {
+                        transitionTo(mStaDisabledWithScanState);
+                    }
                 default:
                     return NOT_HANDLED;
             }
diff --git a/service/java/com/android/server/wifi/WifiLogger.java b/service/java/com/android/server/wifi/WifiLogger.java
new file mode 100644
index 0000000..8add779
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiLogger.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2010 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 com.android.internal.app.IBatteryStats;
+import com.android.internal.util.Protocol;
+
+import android.support.v4.util.CircularArray;
+import android.util.Base64;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.zip.Deflater;
+
+/**
+ * Tracks various logs for framework
+ */
+class WifiLogger  {
+
+    private static final String TAG = "WifiLogger";
+    private static final boolean DBG = false;
+
+    /** log level flags; keep these consistent with wifi_logger.h */
+
+    /** No logs whatsoever */
+    public static final int VERBOSE_NO_LOG = 0;
+    /** No logs whatsoever */
+    public static final int VERBOSE_NORMAL_LOG = 1;
+    /** Be careful since this one can affect performance and power */
+    public static final int VERBOSE_LOG_WITH_WAKEUP  = 2;
+    /** Be careful since this one can affect performance and power and memory */
+    public static final int VERBOSE_DETAILED_LOG_WITH_WAKEUP  = 3;
+
+    /** ring buffer flags; keep these consistent with wifi_logger.h */
+    public static final int RING_BUFFER_FLAG_HAS_BINARY_ENTRIES     = 0x00000001;
+    public static final int RING_BUFFER_FLAG_HAS_ASCII_ENTRIES      = 0x00000002;
+    public static final int RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES = 0x00000004;
+
+    /** various reason codes */
+    public static final int REPORT_REASON_NONE                      = 0;
+    public static final int REPORT_REASON_ASSOC_FAILURE             = 1;
+    public static final int REPORT_REASON_AUTH_FAILURE              = 2;
+    public static final int REPORT_REASON_AUTOROAM_FAILURE          = 3;
+    public static final int REPORT_REASON_DHCP_FAILURE              = 4;
+    public static final int REPORT_REASON_UNEXPECTED_DISCONNECT     = 5;
+    public static final int REPORT_REASON_SCAN_FAILURE              = 6;
+    public static final int REPORT_REASON_USER_ACTION               = 7;
+
+    /** number of ring buffer entries to cache */
+    public static final int MAX_RING_BUFFERS                        = 10;
+
+    /** number of bug reports to hold */
+    public static final int MAX_BUG_REPORTS                         = 4;
+
+    /** number of alerts to hold */
+    public static final int MAX_ALERT_REPORTS                       = 1;
+
+    /** minimum wakeup interval for each of the log levels */
+    private static final int MinWakeupIntervals[] = new int[] { 0, 3600, 60, 10 };
+    /** minimum buffer size for each of the log levels */
+    private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 };
+
+    private int mLogLevel = VERBOSE_NO_LOG;
+    private String mFirmwareVersion;
+    private String mDriverVersion;
+    private int mSupportedFeatureSet;
+    private WifiNative.RingBufferStatus[] mRingBuffers;
+    private WifiNative.RingBufferStatus mPerPacketRingBuffer;
+    private WifiStateMachine mWifiStateMachine;
+
+    public WifiLogger(WifiStateMachine wifiStateMachine) {
+        mWifiStateMachine = wifiStateMachine;
+    }
+
+    public synchronized void startLogging(boolean verboseEnabled) {
+        mFirmwareVersion = WifiNative.getFirmwareVersion();
+        mDriverVersion = WifiNative.getDriverVersion();
+        mSupportedFeatureSet = WifiNative.getSupportedLoggerFeatureSet();
+
+        if (mLogLevel == VERBOSE_NO_LOG)
+            WifiNative.setLoggingEventHandler(mHandler);
+
+        if (verboseEnabled) {
+            mLogLevel = VERBOSE_LOG_WITH_WAKEUP;
+        } else {
+            mLogLevel = VERBOSE_NORMAL_LOG;
+        }
+        if (mRingBuffers == null) {
+            if (fetchRingBuffers()) {
+                startLoggingAllExceptPerPacketBuffers();
+            }
+        }
+    }
+
+    public synchronized void startPacketLog() {
+        if (mPerPacketRingBuffer != null) {
+            startLoggingRingBuffer(mPerPacketRingBuffer);
+        } else {
+            if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+        }
+    }
+
+    public synchronized void stopPacketLog() {
+        if (mPerPacketRingBuffer != null) {
+            stopLoggingRingBuffer(mPerPacketRingBuffer);
+        } else {
+            if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+        }
+    }
+
+    public synchronized void stopLogging() {
+        if (mLogLevel != VERBOSE_NO_LOG) {
+            //resetLogHandler only can be used when you terminate all logging since all handler will
+            //be removed. This also stop alert logging
+            if(!WifiNative.resetLogHandler()) {
+                Log.e(TAG, "Fail to reset log handler");
+            } else {
+                if (DBG) Log.d(TAG,"Reset log handler");
+            }
+            stopLoggingAllBuffers();
+            mRingBuffers = null;
+            mLogLevel = VERBOSE_NO_LOG;
+        }
+    }
+
+    public synchronized void captureBugReportData(int reason) {
+        BugReport report = captureBugreport(reason, true);
+        mLastBugReports.addLast(report);
+    }
+
+    public synchronized void captureAlertData(int errorCode, byte[] alertData) {
+        BugReport report = captureBugreport(errorCode, /* captureFWDump = */ true);
+        report.alertData = alertData;
+        mLastAlerts.addLast(report);
+    }
+
+    public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Chipset information :-----------------------------------------------");
+        pw.println("FW Version is: " + mFirmwareVersion);
+        pw.println("Driver Version is: " + mDriverVersion);
+        pw.println("Supported Feature set: " + mSupportedFeatureSet);
+
+        for (int i = 0; i < mLastAlerts.size(); i++) {
+            pw.println("--------------------------------------------------------------------");
+            pw.println("Alert dump " + i);
+            pw.print(mLastAlerts.get(i));
+            pw.println("--------------------------------------------------------------------");
+        }
+
+        for (int i = 0; i < mLastBugReports.size(); i++) {
+            pw.println("--------------------------------------------------------------------");
+            pw.println("Bug dump " + i);
+            pw.print(mLastBugReports.get(i));
+            pw.println("--------------------------------------------------------------------");
+        }
+
+        pw.println("--------------------------------------------------------------------");
+    }
+
+    /* private methods and data */
+    private static class BugReport {
+        long systemTimeMs;
+        long kernelTimeNanos;
+        int errorCode;
+        HashMap<String, byte[][]> ringBuffers = new HashMap();
+        byte[] fwMemoryDump;
+        byte[] alertData;
+
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+
+            Calendar c = Calendar.getInstance();
+            c.setTimeInMillis(systemTimeMs);
+            builder.append("system time = ").append(
+                    String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)).append("\n");
+
+            long kernelTimeMs = kernelTimeNanos/(1000*1000);
+            builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
+                    (kernelTimeMs%1000).append("\n");
+
+            if (alertData == null)
+                builder.append("reason = ").append(errorCode).append("\n");
+            else {
+                builder.append("errorCode = ").append(errorCode);
+                builder.append("data \n");
+                builder.append(compressToBase64(alertData)).append("\n");
+            }
+
+            for (HashMap.Entry<String, byte[][]> e : ringBuffers.entrySet()) {
+                String ringName = e.getKey();
+                byte[][] buffers = e.getValue();
+                builder.append("ring-buffer = ").append(ringName).append("\n");
+
+                int size = 0;
+                for (int i = 0; i < buffers.length; i++) {
+                    size += buffers[i].length;
+                }
+
+                byte[] buffer = new byte[size];
+                int index = 0;
+                for (int i = 0; i < buffers.length; i++) {
+                    System.arraycopy(buffers[i], 0, buffer, index, buffers[i].length);
+                    index += buffers[i].length;
+                }
+
+                builder.append(compressToBase64(buffer));
+                builder.append("\n");
+            }
+
+            if (fwMemoryDump != null) {
+                builder.append("FW Memory dump \n");
+                builder.append(compressToBase64(fwMemoryDump));
+            }
+
+            return builder.toString();
+        }
+    }
+
+    static class LimitedCircularArray<E> {
+        private CircularArray<E> mArray;
+        private int mMax;
+        LimitedCircularArray(int max) {
+            mArray = new CircularArray<E>();
+            mMax = max;
+        }
+
+        public final void addLast(E e) {
+            if (mArray.size() >= mMax)
+                mArray.popFirst();
+            mArray.addLast(e);
+        }
+
+        public final int size() {
+            return mArray.size();
+        }
+
+        public final E get(int i) {
+            return mArray.get(i);
+        }
+    }
+
+    private final LimitedCircularArray<BugReport> mLastAlerts =
+            new LimitedCircularArray<BugReport>(MAX_ALERT_REPORTS);
+    private final LimitedCircularArray<BugReport> mLastBugReports =
+            new LimitedCircularArray<BugReport>(MAX_BUG_REPORTS);
+    private final HashMap<String, LimitedCircularArray<byte[]>> mRingBufferData = new HashMap();
+
+    private final WifiNative.WifiLoggerEventHandler mHandler =
+            new WifiNative.WifiLoggerEventHandler() {
+        @Override
+        public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
+            WifiLogger.this.onRingBufferData(status, buffer);
+        }
+
+        @Override
+        public void onWifiAlert(int errorCode, byte[] buffer) {
+            WifiLogger.this.onWifiAlert(errorCode, buffer);
+        }
+    };
+
+    synchronized void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
+        LimitedCircularArray<byte[]> ring = mRingBufferData.get(status.name);
+        if (ring != null) {
+            ring.addLast(buffer);
+        }
+    }
+
+    synchronized void onWifiAlert(int errorCode, byte[] buffer) {
+        if (mWifiStateMachine != null) {
+            mWifiStateMachine.sendMessage(
+                    WifiStateMachine.CMD_FIRMWARE_ALERT, errorCode, 0, buffer);
+        }
+    }
+
+    private boolean fetchRingBuffers() {
+        if (mRingBuffers != null) return true;
+
+        mRingBuffers = WifiNative.getRingBufferStatus();
+        if (mRingBuffers != null) {
+            for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+                if (DBG) Log.d(TAG, "RingBufferStatus is: \n" + buffer.name);
+                if (mRingBufferData.containsKey(buffer.name) == false) {
+                    mRingBufferData.put(buffer.name,
+                            new LimitedCircularArray<byte[]>(MAX_RING_BUFFERS));
+                }
+                if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
+                    mPerPacketRingBuffer = buffer;
+                }
+            }
+        } else {
+            Log.e(TAG, "no ring buffers found");
+        }
+
+        return mRingBuffers != null;
+    }
+
+    private boolean startLoggingAllExceptPerPacketBuffers() {
+
+        if (mRingBuffers == null) {
+            if (DBG) Log.d(TAG, "No ring buffers to log anything!");
+            return false;
+        }
+
+        for (WifiNative.RingBufferStatus buffer : mRingBuffers){
+
+            if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
+                /* skip per-packet-buffer */
+                if (DBG) Log.d(TAG, "skipped per packet logging ring " + buffer.name);
+                continue;
+            }
+
+            startLoggingRingBuffer(buffer);
+        }
+
+        return true;
+    }
+
+    private boolean startLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
+
+        int minInterval = MinWakeupIntervals[mLogLevel];
+        int minDataSize = MinBufferSizes[mLogLevel];
+
+        if (WifiNative.startLoggingRingBuffer(
+                mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) {
+            if (DBG) Log.e(TAG, "Could not start logging ring " + buffer.name);
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
+        if (WifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) {
+            if (DBG) Log.e(TAG, "Could not stop logging ring " + buffer.name);
+        }
+        return true;
+    }
+
+    private boolean stopLoggingAllBuffers() {
+        if (mRingBuffers != null) {
+            for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+                stopLoggingRingBuffer(buffer);
+            }
+        }
+        return true;
+    }
+
+    private boolean getAllRingBufferData() {
+        if (mRingBuffers == null) {
+            Log.e(TAG, "Not ring buffers available to collect data!");
+            return false;
+        }
+
+        for (WifiNative.RingBufferStatus element : mRingBuffers){
+            boolean result = WifiNative.getRingBufferData(element.name);
+            if (!result) {
+                Log.e(TAG, "Fail to get ring buffer data of: " + element.name);
+                return false;
+            }
+        }
+
+        Log.d(TAG, "getAllRingBufferData Successfully!");
+        return true;
+    }
+
+    private BugReport captureBugreport(int errorCode, boolean captureFWDump) {
+        BugReport report = new BugReport();
+        report.errorCode = errorCode;
+        report.systemTimeMs = System.currentTimeMillis();
+        report.kernelTimeNanos = System.nanoTime();
+
+        if (mRingBuffers != null) {
+            for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+                /* this will push data in mRingBuffers */
+                WifiNative.getRingBufferData(buffer.name);
+                LimitedCircularArray<byte[]> data = mRingBufferData.get(buffer.name);
+                byte[][] buffers = new byte[data.size()][];
+                for (int i = 0; i < data.size(); i++) {
+                    buffers[i] = data.get(i).clone();
+                }
+                report.ringBuffers.put(buffer.name, buffers);
+            }
+        }
+
+        if (captureFWDump) {
+            report.fwMemoryDump = WifiNative.getFwMemoryDump();
+        }
+        return report;
+    }
+
+    private static String compressToBase64(byte[] input) {
+        String result;
+        //compress
+        Deflater compressor = new Deflater();
+        compressor.setLevel(Deflater.BEST_COMPRESSION);
+        compressor.setInput(input);
+        compressor.finish();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
+        final byte[] buf = new byte[1024];
+
+        while (!compressor.finished()) {
+            int count = compressor.deflate(buf);
+            bos.write(buf, 0, count);
+        }
+
+        try {
+            compressor.end();
+            bos.close();
+        } catch (IOException e) {
+            Log.e(TAG, "ByteArrayOutputStream close error");
+            result =  android.util.Base64.encodeToString(input, Base64.DEFAULT);
+            return result;
+        }
+
+        byte[] compressed = bos.toByteArray();
+        if (DBG) {
+            Log.d(TAG," length is:" + (compressed == null? "0" : compressed.length));
+        }
+
+        //encode
+        result = android.util.Base64.encodeToString(
+                compressed.length < input.length ? compressed : input , Base64.DEFAULT);
+
+        if (DBG) {
+            Log.d(TAG, "FwMemoryDump length is :" + result.length());
+        }
+
+        return result;
+    }
+}
diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java
index c0aa2f6..9758d57 100644
--- a/service/java/com/android/server/wifi/WifiMonitor.java
+++ b/service/java/com/android/server/wifi/WifiMonitor.java
@@ -27,10 +27,11 @@
 import android.net.wifi.p2p.WifiP2pProvDiscEvent;
 import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
 import android.os.Message;
-import android.os.SystemClock;
 import android.text.TextUtils;
+import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.server.wifi.hotspot2.Utils;
 import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
 
 import com.android.internal.util.Protocol;
@@ -66,9 +67,10 @@
     private static final int ASSOC_REJECT = 9;
     private static final int SSID_TEMP_DISABLE = 10;
     private static final int SSID_REENABLE = 11;
-    private static final int BSS_ADDED = 12;
-    private static final int BSS_REMOVED = 13;
+    private static final int BSS_ADDED    = 12;
+    private static final int BSS_REMOVED  = 13;
     private static final int UNKNOWN      = 14;
+    private static final int SCAN_FAILED  = 15;
 
     /** All events coming from the supplicant start with this prefix */
     private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
@@ -158,6 +160,13 @@
 
     /**
      * <pre>
+     * CTRL-EVENT-SCAN-FAILED ret=code[ retry=1]
+     * </pre>
+     */
+    private static final String SCAN_FAILED_STR =  "SCAN-FAILED";
+
+    /**
+     * <pre>
      * CTRL-EVENT-LINK-SPEED x Mb/s
      * </pre>
      * {@code x} is the link speed in Mb/sec.
@@ -189,6 +198,10 @@
      */
     private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
 
+    /* EAP authentication timeout events */
+    private static final String AUTH_EVENT_PREFIX_STR = "Authentication with";
+    private static final String AUTH_TIMEOUT_STR = "timed out.";
+
     /**
      * This indicates an assoc reject event
      */
@@ -277,6 +290,19 @@
             Pattern.compile("SIM-([0-9]*):GSM-AUTH((:[0-9a-f]+)+) needed for SSID (.+)");
 
     /**
+     * Regex pattern for extracting an external 3G sim authentication request from a string.
+     * Matches a strings like the following:<pre>
+     * CTRL-REQ-SIM-<network id>:UMTS-AUTH:<RAND>:<AUTN> needed for SSID <SSID>
+     * This pattern should find
+     *    1 - id
+     *    2 - Rand
+     *    3 - Autn
+     *    4 - SSID
+     */
+    private static Pattern mRequestUmtsAuthPattern =
+            Pattern.compile("SIM-([0-9]*):UMTS-AUTH:([0-9a-f]+):([0-9a-f]+) needed for SSID (.+)");
+
+    /**
      * Regex pattern for extracting SSIDs from request identity string.
      * Matches a strings like the following:<pre>
      * CTRL-REQ-IDENTITY-xx:Identity needed for SSID XXXX</pre>
@@ -396,6 +422,7 @@
     private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
     /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
     private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
+    private static final String ANQP_DONE_STR = "ANQP-QUERY-DONE";
 
     /* Supplicant events reported to a state machine */
     private static final int BASE = Protocol.BASE_WIFI_MONITOR;
@@ -436,6 +463,8 @@
     /* Request SIM Auth */
     public static final int SUP_REQUEST_SIM_AUTH                 = BASE + 16;
 
+    public static final int SCAN_FAILED_EVENT                    = BASE + 17;
+
     /* P2P events */
     public static final int P2P_DEVICE_FOUND_EVENT               = BASE + 21;
     public static final int P2P_DEVICE_LOST_EVENT                = BASE + 22;
@@ -462,6 +491,7 @@
 
     /* Indicates assoc reject event */
     public static final int ASSOCIATION_REJECTION_EVENT          = BASE + 43;
+    public static final int ANQP_DONE_EVENT                      = BASE + 44;
 
     /* hotspot 2.0 ANQP events */
     public static final int GAS_QUERY_START_EVENT                = BASE + 51;
@@ -569,8 +599,8 @@
                     if (mWifiNative.connectToSupplicant()) {
                         m.mMonitoring = true;
                         m.mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
-                        new MonitorThread(mWifiNative, this).start();
                         mConnected = true;
+                        new MonitorThread(mWifiNative, this).start();
                         break;
                     }
                     if (connectTries++ < 5) {
@@ -579,7 +609,6 @@
                         } catch (InterruptedException ignore) {
                         }
                     } else {
-                        mIfaceMap.remove(iface);
                         m.mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
                         Log.e(TAG, "startMonitoring(" + iface + ") failed!");
                         break;
@@ -633,6 +662,7 @@
 
         private synchronized boolean dispatchEvent(String eventStr) {
             String iface;
+            // IFNAME=wlan0 ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
             if (eventStr.startsWith("IFNAME=")) {
                 int space = eventStr.indexOf(' ');
                 if (space != -1) {
@@ -705,6 +735,7 @@
     private static class MonitorThread extends Thread {
         private final WifiNative mWifiNative;
         private final WifiMonitorSingleton mWifiMonitorSingleton;
+        private final LocalLog mLocalLog = WifiNative.getLocalLog();
 
         public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
             super("WifiMonitor");
@@ -713,13 +744,23 @@
         }
 
         public void run() {
+            if (DBG) {
+                Log.d(TAG, "MonitorThread start with mConnected=" +
+                     mWifiMonitorSingleton.mConnected);
+            }
             //noinspection InfiniteLoopStatement
             for (;;) {
+                if (!mWifiMonitorSingleton.mConnected) {
+                    if (DBG) Log.d(TAG, "MonitorThread exit because mConnected is false");
+                    break;
+                }
                 String eventStr = mWifiNative.waitForEvent();
 
-                // Skip logging the common but mostly uninteresting scan-results event
-                if (DBG && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
-                    Log.d(TAG, "Event [" + eventStr + "]");
+                // Skip logging the common but mostly uninteresting events
+                if (eventStr.indexOf(BSS_ADDED_STR) == -1
+                        && eventStr.indexOf(BSS_REMOVED_STR) == -1) {
+                    if (DBG) Log.d(TAG, "Event [" + eventStr + "]");
+                    mLocalLog.log("Event [" + eventStr + "]");
                 }
 
                 if (mWifiMonitorSingleton.dispatchEvent(eventStr)) {
@@ -764,13 +805,20 @@
                 handleP2pEvents(eventStr);
             } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
                 handleHostApEvents(eventStr);
-            } else if (eventStr.startsWith(GAS_QUERY_PREFIX_STR)) {
+            } else if (eventStr.startsWith(ANQP_DONE_STR)) {
+                try {
+                    handleAnqpResult(eventStr);
+                }
+                catch (IllegalArgumentException iae) {
+                    Log.e(TAG, "Bad ANQP event string: '" + eventStr + "': " + iae);
+                }
+            } else if (eventStr.startsWith(GAS_QUERY_PREFIX_STR)) {        // !!! clean >>End
                 handleGasQueryEvents(eventStr);
             } else if (eventStr.startsWith(RX_HS20_ANQP_ICON_STR)) {
                 if (mStateMachine2 != null)
                     mStateMachine2.sendMessage(RX_HS20_ANQP_ICON_EVENT,
                             eventStr.substring(RX_HS20_ANQP_ICON_STR_LEN + 1));
-            } else if (eventStr.startsWith(HS20_PREFIX_STR)) {
+            } else if (eventStr.startsWith(HS20_PREFIX_STR)) {                  // !!! <<End
                 handleHs20Events(eventStr);
             } else if (eventStr.startsWith(REQUEST_PREFIX_STR)) {
                 handleRequests(eventStr);
@@ -778,6 +826,9 @@
                 handleTargetBSSIDEvent(eventStr);
             } else if (eventStr.startsWith(ASSOCIATED_WITH_STR)) {
                 handleAssociatedBSSIDEvent(eventStr);
+            } else if (eventStr.startsWith(AUTH_EVENT_PREFIX_STR) &&
+                    eventStr.endsWith(AUTH_TIMEOUT_STR)) {
+                mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
             } else {
                 if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
             }
@@ -806,6 +857,8 @@
             event = STATE_CHANGE;
         else if (eventName.equals(SCAN_RESULTS_STR))
             event = SCAN_RESULTS;
+        else if (eventName.equals(SCAN_FAILED_STR))
+            event = SCAN_FAILED;
         else if (eventName.equals(LINK_SPEED_STR))
             event = LINK_SPEED;
         else if (eventName.equals(TERMINATING_STR))
@@ -824,8 +877,7 @@
             event = BSS_ADDED;
         } else if (eventName.equals(BSS_REMOVED_STR)) {
             event = BSS_REMOVED;
-        }
-        else
+        } else
             event = UNKNOWN;
 
         String eventData = eventStr;
@@ -958,6 +1010,10 @@
                 mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
                 break;
 
+            case SCAN_FAILED:
+                mStateMachine.sendMessage(SCAN_FAILED_EVENT);
+                break;
+
             case UNKNOWN:
                 if (DBG) {
                     logDbg("handleEvent unknown: " + Integer.toString(event) + "  " + remainder);
@@ -1053,14 +1109,34 @@
         return err;
     }
 
+    WifiP2pDevice getWifiP2pDevice(String dataString) {
+        try {
+            WifiP2pDevice device = new WifiP2pDevice(dataString);
+            return device;
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    WifiP2pGroup getWifiP2pGroup(String dataString) {
+        try {
+            WifiP2pGroup group = new WifiP2pGroup(dataString);
+            return group;
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
     /**
      * Handle p2p events
      */
     private void handleP2pEvents(String dataString) {
         if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
-            mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
+            WifiP2pDevice device = getWifiP2pDevice(dataString);
+            if (device != null) mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, device);
         } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
-            mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
+            WifiP2pDevice device = getWifiP2pDevice(dataString);
+            if (device != null) mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, device);
         } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
             mStateMachine.sendMessage(P2P_FIND_STOPPED_EVENT);
         } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
@@ -1075,9 +1151,11 @@
         } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
             mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
         } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
-            mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
+            WifiP2pGroup group = getWifiP2pGroup(dataString);
+            if (group != null) mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, group);
         } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
-            mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString));
+            WifiP2pGroup group = getWifiP2pGroup(dataString);
+            if (group != null) mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, group);
         } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
             mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
                     new WifiP2pGroup(dataString));
@@ -1121,6 +1199,38 @@
         }
     }
 
+    private static final String ADDR_STRING = "addr=";
+    private static final String RESULT_STRING = "result=";
+
+    // ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
+
+    private void handleAnqpResult(String eventStr) {
+        int addrPos = eventStr.indexOf(ADDR_STRING);
+        int resPos = eventStr.indexOf(RESULT_STRING);
+        if (addrPos < 0 || resPos < 0) {
+            throw new IllegalArgumentException("Unexpected ANQP result notification");
+        }
+        int eoaddr = eventStr.indexOf(' ', addrPos + ADDR_STRING.length());
+        if (eoaddr < 0) {
+            eoaddr = eventStr.length();
+        }
+        int eoresult = eventStr.indexOf(' ', resPos + RESULT_STRING.length());
+        if (eoresult < 0) {
+            eoresult = eventStr.length();
+        }
+
+        try {
+            long bssid = Utils.parseMac(eventStr.substring(addrPos + ADDR_STRING.length(), eoaddr));
+            int result = eventStr.substring(
+                    resPos + RESULT_STRING.length(), eoresult).equalsIgnoreCase("success") ? 1 : 0;
+
+            mStateMachine.sendMessage(ANQP_DONE_EVENT, result, 0, bssid);
+        }
+        catch (IllegalArgumentException iae) {
+            Log.e(TAG, "Bad MAC address in ANQP response: " + iae.getMessage());
+        }
+    }
+
     /**
      * Handle ANQP events
      */
@@ -1209,14 +1319,23 @@
             }
             mStateMachine.sendMessage(SUP_REQUEST_IDENTITY, eventLogCounter, reason, SSID);
         } else if (requestName.startsWith(SIM_STR)) {
-            Matcher match = mRequestGsmAuthPattern.matcher(requestName);
-            if (match.find()) {
-                WifiStateMachine.SimAuthRequestData data =
-                        new WifiStateMachine.SimAuthRequestData();
-                data.networkId = Integer.parseInt(match.group(1));
+            Matcher matchGsm = mRequestGsmAuthPattern.matcher(requestName);
+            Matcher matchUmts = mRequestUmtsAuthPattern.matcher(requestName);
+            WifiStateMachine.SimAuthRequestData data =
+                    new WifiStateMachine.SimAuthRequestData();
+            if (matchGsm.find()) {
+                data.networkId = Integer.parseInt(matchGsm.group(1));
                 data.protocol = WifiEnterpriseConfig.Eap.SIM;
-                data.ssid = match.group(4);
-                data.challenges = match.group(2).split(":");
+                data.ssid = matchGsm.group(4);
+                data.data = matchGsm.group(2).split(":");
+                mStateMachine.sendMessage(SUP_REQUEST_SIM_AUTH, data);
+            } else if (matchUmts.find()) {
+                data.networkId = Integer.parseInt(matchUmts.group(1));
+                data.protocol = WifiEnterpriseConfig.Eap.AKA;
+                data.ssid = matchUmts.group(4);
+                data.data = new String[2];
+                data.data[0] = matchUmts.group(2);
+                data.data[1] = matchUmts.group(3);
                 mStateMachine.sendMessage(SUP_REQUEST_SIM_AUTH, data);
             } else {
                 Log.e(TAG, "couldn't parse SIM auth request - " + requestName);
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index e8c501c..899529d 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -19,21 +19,35 @@
 import android.net.wifi.BatchedScanSettings;
 import android.net.wifi.RttManager;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
+import android.net.wifi.RttManager;
+import android.net.wifi.WifiSsid;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.p2p.WifiP2pConfig;
 import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.WifiEnterpriseConfig;
 import android.os.SystemClock;
 import android.text.TextUtils;
-import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.util.Base64;
 import android.util.LocalLog;
 import android.util.Log;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-
+import java.util.zip.Deflater;
+import libcore.util.HexEncoding;
 /**
  * Native calls for bring up/shut down of the supplicant daemon and for
  * sending requests to the supplicant daemon
@@ -65,6 +79,10 @@
 
     private boolean mSuspendOptEnabled = false;
 
+    private static final int EID_HT_OPERATION = 61;
+    private static final int EID_VHT_OPERATION = 192;
+    private static final int EID_EXTENDED_CAPS = 127;
+    private static final int RTT_RESP_ENABLE_BIT = 70;
     /* Register native functions */
 
     static {
@@ -122,12 +140,12 @@
         }
     }
 
-    private static final LocalLog mLocalLog = new LocalLog(1024);
+    private static final LocalLog mLocalLog = new LocalLog(16384);
 
     // hold mLock before accessing mCmdIdLock
     private static int sCmdId;
 
-    public LocalLog getLocalLog() {
+    public static LocalLog getLocalLog() {
         return mLocalLog;
     }
 
@@ -171,6 +189,16 @@
         }
     }
 
+    private boolean doBooleanCommandWithoutLogging(String command) {
+        if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
+        synchronized (mLock) {
+            int cmdId = getNewCmdIdLocked();
+            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
+            if (DBG) Log.d(mTAG, command + ": returned " + result);
+            return result;
+        }
+    }
+
     private int doIntCommand(String command) {
         if (DBG) Log.d(mTAG, "doInt: " + command);
         synchronized (mLock) {
@@ -267,7 +295,12 @@
 
     public boolean setNetworkVariable(int netId, String name, String value) {
         if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
-        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
+        if (name.equals(WifiConfiguration.pskVarName)
+                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
+            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
+        } else {
+            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
+        }
     }
 
     public String getNetworkVariable(int netId, String name) {
@@ -308,6 +341,11 @@
         return doBooleanCommand("DISABLE_NETWORK " + netId);
     }
 
+    public boolean selectNetwork(int netId) {
+        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
+        return doBooleanCommand("SELECT_NETWORK " + netId);
+    }
+
     public boolean reconnect() {
         if (DBG) logDbg("RECONNECT ");
         return doBooleanCommand("RECONNECT");
@@ -345,6 +383,8 @@
         return null;
     }
 
+
+
     /**
      * Format of results:
      * =================
@@ -361,9 +401,29 @@
      * RANGE=ALL gets all scan results
      * RANGE=ID- gets results from ID
      * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
+     * 0                         0                        1                       0     2
+     *                           WPA_BSS_MASK_MESH_SCAN | WPA_BSS_MASK_DELIM    | WPA_BSS_MASK_WIFI_DISPLAY
+     * 0                         0                        0                       1     1   -> 9
+     * WPA_BSS_MASK_INTERNETW  | WPA_BSS_MASK_P2P_SCAN  | WPA_BSS_MASK_WPS_SCAN | WPA_BSS_MASK_SSID
+     * 1                         0                        0                       1     9   -> d
+     * WPA_BSS_MASK_FLAGS      | WPA_BSS_MASK_IE        | WPA_BSS_MASK_AGE      | WPA_BSS_MASK_TSF
+     * 1                         0                        0                       0     8
+     * WPA_BSS_MASK_LEVEL      | WPA_BSS_MASK_NOISE     | WPA_BSS_MASK_QUAL     | WPA_BSS_MASK_CAPABILITIES
+     * 0                         1                        1                       1     7
+     * WPA_BSS_MASK_BEACON_INT | WPA_BSS_MASK_FREQ      | WPA_BSS_MASK_BSSID    | WPA_BSS_MASK_ID
+     *
+     * WPA_BSS_MASK_INTERNETW adds ANQP info (ctrl_iface:4151-4176)
+     *
+     * ctrl_iface.c:wpa_supplicant_ctrl_iface_process:7884
+     *  wpa_supplicant_ctrl_iface_bss:4315
+     *  print_bss_info
      */
     public String scanResults(int sid) {
-        return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987");
+        return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x29d87");
+    }
+
+    public String doCustomCommand(String command) {
+        return doStringCommand(command);
     }
 
     /**
@@ -510,22 +570,24 @@
             && doBooleanCommand("DRIVER RXFILTER-START");
     }
 
-    public int getBand() {
-       String ret = doStringCommand("DRIVER GETBAND");
-        if (!TextUtils.isEmpty(ret)) {
-            //reply is "BAND X" where X is the band
-            String[] tokens = ret.split(" ");
-            try {
-                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
-            } catch (NumberFormatException e) {
-                return -1;
-            }
-        }
-        return -1;
-    }
-
+    /**
+     * Set the operational frequency band
+     * @param band One of
+     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
+     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
+     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
+     * @return {@code true} if the operation succeeded, {@code false} otherwise
+     */
     public boolean setBand(int band) {
-        return doBooleanCommand("DRIVER SETBAND " + band);
+        String bandstr;
+
+        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
+            bandstr = "5G";
+        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
+            bandstr = "2G";
+        else
+            bandstr = "AUTO";
+        return doBooleanCommand("SET SETBAND " + bandstr);
     }
 
     /**
@@ -586,15 +648,20 @@
     }
 
     public boolean setCountryCode(String countryCode) {
-        return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
+        if (countryCode != null)
+            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
+        else
+            return doBooleanCommand("DRIVER COUNTRY");
     }
 
-    public void enableBackgroundScan(boolean enable) {
+    public boolean enableBackgroundScan(boolean enable) {
+        boolean ret;
         if (enable) {
-            doBooleanCommand("SET pno 1");
+            ret = doBooleanCommand("SET pno 1");
         } else {
-            doBooleanCommand("SET pno 0");
+            ret = doBooleanCommand("SET pno 0");
         }
+        return ret;
     }
 
     public void enableAutoConnect(boolean enable) {
@@ -697,9 +764,10 @@
         }
     }
 
-    public boolean simAuthResponse(int id, String response) {
+    public boolean simAuthResponse(int id, String type, String response) {
+        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
         synchronized (mLock) {
-            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-AUTH" + response);
+            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
         }
     }
 
@@ -1138,379 +1206,6 @@
         return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
     }
 
-    /* WIFI HAL support */
-
-    private static final String TAG = "WifiNative-HAL";
-    private static long sWifiHalHandle = 0;  /* used by JNI to save wifi_handle */
-    private static long[] sWifiIfaceHandles = null;  /* used by JNI to save interface handles */
-    private static int sWlan0Index = -1;
-    private static int sP2p0Index = -1;
-
-    private static boolean sHalIsStarted = false;
-    private static boolean sHalFailed = false;
-
-    private static native boolean startHalNative();
-    private static native void stopHalNative();
-    private static native void waitForHalEventNative();
-
-    private static class MonitorThread extends Thread {
-        public void run() {
-            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
-            waitForHalEventNative();
-        }
-    }
-
-    synchronized public static boolean startHal() {
-        Log.i(TAG, "startHal");
-        synchronized (mLock) {
-            if (sHalIsStarted)
-                return true;
-            if (sHalFailed)
-                return false;
-            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
-                new MonitorThread().start();
-                sHalIsStarted = true;
-                return true;
-            } else {
-                Log.i(TAG, "Could not start hal");
-                sHalIsStarted = false;
-                sHalFailed = true;
-                return false;
-            }
-        }
-    }
-
-    synchronized public static void stopHal() {
-        stopHalNative();
-    }
-
-    private static native int getInterfacesNative();
-
-    synchronized public static int getInterfaces() {
-        synchronized (mLock) {
-            if (sWifiIfaceHandles == null) {
-                int num = getInterfacesNative();
-                int wifi_num = 0;
-                for (int i = 0; i < num; i++) {
-                    String name = getInterfaceNameNative(i);
-                    Log.i(TAG, "interface[" + i + "] = " + name);
-                    if (name.equals("wlan0")) {
-                        sWlan0Index = i;
-                        wifi_num++;
-                    } else if (name.equals("p2p0")) {
-                        sP2p0Index = i;
-                        wifi_num++;
-                    }
-                }
-                return wifi_num;
-            } else {
-                return sWifiIfaceHandles.length;
-            }
-        }
-    }
-
-    private static native String getInterfaceNameNative(int index);
-    synchronized public static String getInterfaceName(int index) {
-        return getInterfaceNameNative(index);
-    }
-
-    public static class ScanCapabilities {
-        public int  max_scan_cache_size;                 // in number of scan results??
-        public int  max_scan_buckets;
-        public int  max_ap_cache_per_scan;
-        public int  max_rssi_sample_size;
-        public int  max_scan_reporting_threshold;        // in number of scan results??
-        public int  max_hotlist_aps;
-        public int  max_significant_wifi_change_aps;
-    }
-
-    public static boolean getScanCapabilities(ScanCapabilities capabilities) {
-        return getScanCapabilitiesNative(sWlan0Index, capabilities);
-    }
-
-    private static native boolean getScanCapabilitiesNative(
-            int iface, ScanCapabilities capabilities);
-
-    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
-    private static native boolean stopScanNative(int iface, int id);
-    private static native ScanResult[] getScanResultsNative(int iface, boolean flush);
-    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
-
-    public static class ChannelSettings {
-        int frequency;
-        int dwell_time_ms;
-        boolean passive;
-    }
-
-    public static class BucketSettings {
-        int bucket;
-        int band;
-        int period_ms;
-        int report_events;
-        int num_channels;
-        ChannelSettings channels[];
-    }
-
-    public static class ScanSettings {
-        int base_period_ms;
-        int max_ap_per_scan;
-        int report_threshold;
-        int num_buckets;
-        BucketSettings buckets[];
-    }
-
-    public static interface ScanEventHandler {
-        void onScanResultsAvailable();
-        void onFullScanResult(ScanResult fullScanResult);
-        void onSingleScanComplete();
-        void onScanPaused();
-        void onScanRestarted();
-    }
-
-    synchronized static void onScanResultsAvailable(int id) {
-        if (sScanEventHandler  != null) {
-            sScanEventHandler.onScanResultsAvailable();
-        }
-    }
-
-    /* scan status, keep these values in sync with gscan.h */
-    private static int WIFI_SCAN_BUFFER_FULL = 0;
-    private static int WIFI_SCAN_COMPLETE = 1;
-
-    synchronized static void onScanStatus(int status) {
-        Log.i(TAG, "Got a scan status changed event, status = " + status);
-
-        if (status == WIFI_SCAN_BUFFER_FULL) {
-            /* we have a separate event to take care of this */
-        } else if (status == WIFI_SCAN_COMPLETE) {
-            if (sScanEventHandler  != null) {
-                sScanEventHandler.onSingleScanComplete();
-            }
-        }
-    }
-
-    synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
-        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
-                "num = " + bytes.length);
-
-        if (sScanEventHandler == null) {
-            return;
-        }
-
-        int num = 0;
-        for (int i = 0; i < bytes.length; ) {
-            int type  = bytes[i] & 0xFF;
-            int len = bytes[i + 1] & 0xFF;
-
-            if (i + len + 2 > bytes.length) {
-                Log.w(TAG, "bad length " + len + " of IE " + type + " from " + result.BSSID);
-                Log.w(TAG, "ignoring the rest of the IEs");
-                break;
-            }
-            num++;
-            i += len + 2;
-            if (DBG) Log.i(TAG, "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
-                    "next = " + i);
-        }
-
-        ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
-        for (int i = 0, index = 0; i < num; i++) {
-            int type  = bytes[index] & 0xFF;
-            int len = bytes[index + 1] & 0xFF;
-            if (DBG) Log.i(TAG, "index = " + index + ", type = " + type + ", len = " + len);
-            ScanResult.InformationElement elem = new ScanResult.InformationElement();
-            elem.id = type;
-            elem.bytes = new byte[len];
-            for (int j = 0; j < len; j++) {
-                elem.bytes[j] = bytes[index + j + 2];
-            }
-            elements[i] = elem;
-            index += (len + 2);
-        }
-
-        result.informationElements = elements;
-        sScanEventHandler.onFullScanResult(result);
-    }
-
-    private static int sScanCmdId = 0;
-    private static ScanEventHandler sScanEventHandler;
-    private static ScanSettings sScanSettings;
-
-    synchronized public static boolean startScan(
-            ScanSettings settings, ScanEventHandler eventHandler) {
-        synchronized (mLock) {
-
-            if (sScanCmdId != 0) {
-                stopScan();
-            } else if (sScanSettings != null || sScanEventHandler != null) {
-                /* current scan is paused; no need to stop it */
-            }
-
-            sScanCmdId = getNewCmdIdLocked();
-
-            sScanSettings = settings;
-            sScanEventHandler = eventHandler;
-
-            if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
-                sScanEventHandler = null;
-                sScanSettings = null;
-                return false;
-            }
-
-            return true;
-        }
-    }
-
-    synchronized public static void stopScan() {
-        synchronized (mLock) {
-            stopScanNative(sWlan0Index, sScanCmdId);
-            sScanSettings = null;
-            sScanEventHandler = null;
-            sScanCmdId = 0;
-        }
-    }
-
-    synchronized public static void pauseScan() {
-        synchronized (mLock) {
-            if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
-                Log.d(TAG, "Pausing scan");
-                stopScanNative(sWlan0Index, sScanCmdId);
-                sScanCmdId = 0;
-                sScanEventHandler.onScanPaused();
-            }
-        }
-    }
-
-    synchronized public static void restartScan() {
-        synchronized (mLock) {
-            if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
-                Log.d(TAG, "Restarting scan");
-                startScan(sScanSettings, sScanEventHandler);
-                sScanEventHandler.onScanRestarted();
-            }
-        }
-    }
-
-    synchronized public static ScanResult[] getScanResults() {
-        synchronized (mLock) {
-            return getScanResultsNative(sWlan0Index, /* flush = */ false);
-        }
-    }
-
-    public static interface HotlistEventHandler {
-        void onHotlistApFound (ScanResult[]result);
-    }
-
-    private static int sHotlistCmdId = 0;
-    private static HotlistEventHandler sHotlistEventHandler;
-
-    private native static boolean setHotlistNative(int iface, int id,
-            WifiScanner.HotlistSettings settings);
-    private native static boolean resetHotlistNative(int iface, int id);
-
-    synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
-                                    HotlistEventHandler eventHandler) {
-        synchronized (mLock) {
-            if (sHotlistCmdId != 0) {
-                return false;
-            } else {
-                sHotlistCmdId = getNewCmdIdLocked();
-            }
-
-            sHotlistEventHandler = eventHandler;
-            if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
-                sHotlistEventHandler = null;
-                return false;
-            }
-
-            return true;
-        }
-    }
-
-    synchronized public static void resetHotlist() {
-        synchronized (mLock) {
-            if (sHotlistCmdId != 0) {
-                resetHotlistNative(sWlan0Index, sHotlistCmdId);
-                sHotlistCmdId = 0;
-                sHotlistEventHandler = null;
-            }
-        }
-    }
-
-    synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
-        synchronized (mLock) {
-            if (sHotlistCmdId != 0) {
-                sHotlistEventHandler.onHotlistApFound(results);
-            } else {
-                /* this can happen because of race conditions */
-                Log.d(TAG, "Ignoring hotlist AP found change");
-            }
-        }
-    }
-
-    public static interface SignificantWifiChangeEventHandler {
-        void onChangesFound(ScanResult[] result);
-    }
-
-    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
-    private static int sSignificantWifiChangeCmdId;
-
-    private static native boolean trackSignificantWifiChangeNative(
-            int iface, int id, WifiScanner.WifiChangeSettings settings);
-    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
-
-    synchronized public static boolean trackSignificantWifiChange(
-            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
-        synchronized (mLock) {
-            if (sSignificantWifiChangeCmdId != 0) {
-                return false;
-            } else {
-                sSignificantWifiChangeCmdId = getNewCmdIdLocked();
-            }
-
-            sSignificantWifiChangeHandler = handler;
-            if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
-                sSignificantWifiChangeHandler = null;
-                return false;
-            }
-
-            return true;
-        }
-    }
-
-    synchronized static void untrackSignificantWifiChange() {
-        synchronized (mLock) {
-            if (sSignificantWifiChangeCmdId != 0) {
-                untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
-                sSignificantWifiChangeCmdId = 0;
-                sSignificantWifiChangeHandler = null;
-            }
-        }
-    }
-
-    synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
-        synchronized (mLock) {
-            if (sSignificantWifiChangeCmdId != 0) {
-                sSignificantWifiChangeHandler.onChangesFound(results);
-            } else {
-                /* this can happen because of race conditions */
-                Log.d(TAG, "Ignoring significant wifi change");
-            }
-        }
-    }
-
-    synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
-        // TODO: use correct iface name to Index translation
-        if (iface == null) return null;
-        synchronized (mLock) {
-            if (!sHalIsStarted)
-                startHal();
-            if (sHalIsStarted)
-                return getWifiLinkLayerStatsNative(sWlan0Index);
-        }
-        return null;
-    }
-
     /*
      * NFC-related calls
      */
@@ -1534,9 +1229,594 @@
         return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
     }
 
+    /* WIFI HAL support */
+
+    private static final String TAG = "WifiNative-HAL";
+    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
+    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
+    private static int sWlan0Index = -1;
+    private static int sP2p0Index = -1;
+    private static MonitorThread sThread;
+    private static final int STOP_HAL_TIMEOUT_MS = 1000;
+
+    private static native boolean startHalNative();
+    private static native void stopHalNative();
+    private static native void waitForHalEventNative();
+
+    private static class MonitorThread extends Thread {
+        public void run() {
+            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
+            waitForHalEventNative();
+        }
+    }
+
+    synchronized public static boolean startHal() {
+
+        String debugLog = "startHal stack: ";
+        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+        for (int i = 2; i < elements.length && i <= 7; i++ ) {
+            debugLog = debugLog + " - " + elements[i].getMethodName();
+        }
+
+        mLocalLog.log(debugLog);
+
+        synchronized (mLock) {
+            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
+                sThread = new MonitorThread();
+                sThread.start();
+                return true;
+            } else {
+                if (DBG) mLocalLog.log("Could not start hal");
+                Log.e(TAG, "Could not start hal");
+                return false;
+            }
+        }
+    }
+
+    synchronized public static void stopHal() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                stopHalNative();
+                try {
+                    sThread.join(STOP_HAL_TIMEOUT_MS);
+                    Log.d(TAG, "HAL event thread stopped successfully");
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Could not stop HAL cleanly");
+                }
+                sThread = null;
+                sWifiHalHandle = 0;
+                sWifiIfaceHandles = null;
+                sWlan0Index = -1;
+                sP2p0Index = -1;
+            }
+        }
+    }
+
+    public static boolean isHalStarted() {
+        return (sWifiHalHandle != 0);
+    }
+    private static native int getInterfacesNative();
+
+    synchronized public static int getInterfaces() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sWifiIfaceHandles == null) {
+                    int num = getInterfacesNative();
+                    int wifi_num = 0;
+                    for (int i = 0; i < num; i++) {
+                        String name = getInterfaceNameNative(i);
+                        Log.i(TAG, "interface[" + i + "] = " + name);
+                        if (name.equals("wlan0")) {
+                            sWlan0Index = i;
+                            wifi_num++;
+                        } else if (name.equals("p2p0")) {
+                            sP2p0Index = i;
+                            wifi_num++;
+                        }
+                    }
+                    return wifi_num;
+                } else {
+                    return sWifiIfaceHandles.length;
+                }
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private static native String getInterfaceNameNative(int index);
+    synchronized public static String getInterfaceName(int index) {
+        return getInterfaceNameNative(index);
+    }
+
+    public static class ScanCapabilities {
+        public int  max_scan_cache_size;                 // in number of scan results??
+        public int  max_scan_buckets;
+        public int  max_ap_cache_per_scan;
+        public int  max_rssi_sample_size;
+        public int  max_scan_reporting_threshold;        // in number of scan results??
+        public int  max_hotlist_bssids;
+        public int  max_significant_wifi_change_aps;
+    }
+
+    synchronized public static boolean getScanCapabilities(ScanCapabilities capabilities) {
+        synchronized (mLock) {
+            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
+        }
+    }
+
+    private static native boolean getScanCapabilitiesNative(
+            int iface, ScanCapabilities capabilities);
+
+    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
+    private static native boolean stopScanNative(int iface, int id);
+    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
+    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
+    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
+
+    public static class ChannelSettings {
+        int frequency;
+        int dwell_time_ms;
+        boolean passive;
+    }
+
+    public static class BucketSettings {
+        int bucket;
+        int band;
+        int period_ms;
+        int report_events;
+        int num_channels;
+        ChannelSettings channels[];
+    }
+
+    public static class ScanSettings {
+        int base_period_ms;
+        int max_ap_per_scan;
+        int report_threshold_percent;
+        int report_threshold_num_scans;
+        int num_buckets;
+        BucketSettings buckets[];
+    }
+
+    public static interface ScanEventHandler {
+        void onScanResultsAvailable();
+        void onFullScanResult(ScanResult fullScanResult);
+        void onScanStatus();
+        void onScanPaused(WifiScanner.ScanData[] data);
+        void onScanRestarted();
+    }
+
+    synchronized static void onScanResultsAvailable(int id) {
+        if (sScanEventHandler  != null) {
+            sScanEventHandler.onScanResultsAvailable();
+        }
+    }
+
+    /* scan status, keep these values in sync with gscan.h */
+    private static int WIFI_SCAN_BUFFER_FULL = 0;
+    private static int WIFI_SCAN_COMPLETE = 1;
+
+    synchronized static void onScanStatus(int status) {
+        if (status == WIFI_SCAN_BUFFER_FULL) {
+            /* we have a separate event to take care of this */
+        } else if (status == WIFI_SCAN_COMPLETE) {
+            if (sScanEventHandler  != null) {
+                sScanEventHandler.onScanStatus();
+            }
+        }
+    }
+
+    public static  WifiSsid createWifiSsid (byte[] rawSsid) {
+        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
+
+        if (ssidHexString == null) {
+            return null;
+        }
+
+        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
+
+        return wifiSsid;
+    }
+
+    public static String ssidConvert(byte[] rawSsid) {
+        String ssid;
+
+        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
+        try {
+            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
+            ssid = decoded.toString();
+        } catch (CharacterCodingException cce) {
+            ssid = null;
+        }
+
+        if (ssid == null) {
+            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
+        }
+
+        return ssid;
+    }
+
+    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
+        if (rawSsid == null || rawSsid.length == 0 || result == null) {
+            return false;
+        }
+
+        result.SSID = ssidConvert(rawSsid);
+        result.wifiSsid = createWifiSsid(rawSsid);
+        return true;
+    }
+
+    static void populateScanResult(ScanResult result, byte bytes[], String dbg) {
+        int num = 0;
+        if (bytes == null) return;
+        if (dbg == null) dbg = "";
+        for (int i = 0; i < bytes.length - 1; ) {
+            int type  = bytes[i] & 0xFF;
+            int len = bytes[i + 1] & 0xFF;
+            if (i + len + 2 > bytes.length) {
+                Log.w(TAG, dbg + "bad length " + len + " of IE " + type + " from " + result.BSSID);
+                Log.w(TAG, dbg + "ignoring the rest of the IEs");
+                break;
+            }
+            num++;
+            if (DBG) Log.i(TAG, dbg + "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
+                    "next = " + (i + len + 2));
+            i += len + 2;
+        }
+
+        int secondChanelOffset = 0;
+        byte channelMode = 0;
+        byte centerFreqIndex1 = 0;
+        byte centerFreqIndex2 = 0;
+
+        boolean is80211McRTTResponder = false;
+
+        ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
+        for (int i = 0, index = 0; i < num; i++) {
+            int type  = bytes[index] & 0xFF;
+            int len = bytes[index + 1] & 0xFF;
+            if (DBG) Log.i(TAG, dbg + "index = " + index + ", type = " + type + ", len = " + len);
+            ScanResult.InformationElement elem = new ScanResult.InformationElement();
+            elem.id = type;
+            elem.bytes = new byte[len];
+            for (int j = 0; j < len; j++) {
+                elem.bytes[j] = bytes[index + j + 2];
+            }
+            elements[i] = elem;
+            int inforStart = index + 2;
+            index += (len + 2);
+
+            if(type == EID_HT_OPERATION) {
+                secondChanelOffset = bytes[inforStart + 1] & 0x3;
+            } else if(type == EID_VHT_OPERATION) {
+                channelMode = bytes[inforStart];
+                centerFreqIndex1 = bytes[inforStart + 1];
+                centerFreqIndex2 = bytes[inforStart + 2];
+            } else if (type == EID_EXTENDED_CAPS) {
+                int tempIndex = RTT_RESP_ENABLE_BIT / 8;
+                byte offset = RTT_RESP_ENABLE_BIT % 8;
+
+                if(len < tempIndex + 1) {
+                    is80211McRTTResponder = false;
+                } else {
+                    if ((bytes[inforStart + tempIndex] & ((byte)0x1 << offset)) != 0) {
+                        is80211McRTTResponder = true;
+                    } else {
+                        is80211McRTTResponder = false;
+                    }
+                }
+            }
+        }
+
+        if (is80211McRTTResponder) {
+            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+        } else {
+            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
+        }
+
+        //handle RTT related information
+        if (channelMode != 0) {
+            // 80 or 160 MHz
+            result.channelWidth = channelMode + 1;
+
+            //convert channel index to frequency in MHz, channel 36 is 5180MHz
+            result.centerFreq0 = (centerFreqIndex1 - 36) * 5 + 5180;
+
+            if(channelMode > 1) { //160MHz
+                result.centerFreq1 = (centerFreqIndex2 - 36) * 5 + 5180;
+            } else {
+                result.centerFreq1 = 0;
+            }
+        } else {
+            //20 or 40 MHz
+            if (secondChanelOffset != 0) {//40MHz
+                result.channelWidth = 1;
+                if (secondChanelOffset == 1) {
+                    result.centerFreq0 = result.frequency + 20;
+                } else if (secondChanelOffset == 3) {
+                    result.centerFreq0 = result.frequency - 20;
+                } else {
+                    result.centerFreq0 = 0;
+                    Log.e(TAG, dbg + ": Error on secondChanelOffset");
+                }
+            } else {
+                result.centerFreq0  = 0;
+                result.centerFreq1  = 0;
+            }
+            result.centerFreq1  = 0;
+        }
+        if(DBG) {
+            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth +
+                    " PrimaryFreq: " + result.frequency +" mCenterfreq0: " + result.centerFreq0 +
+                    " mCenterfreq1: " + result.centerFreq1 + (is80211McRTTResponder ?
+                    "Support RTT reponder: " : "Do not support RTT responder"));
+        }
+
+        result.informationElements = elements;
+    }
+
+    synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
+        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
+                "num = " + bytes.length);
+
+        if (sScanEventHandler == null) {
+            return;
+        }
+        populateScanResult(result, bytes, " onFullScanResult ");
+
+        sScanEventHandler.onFullScanResult(result);
+    }
+
+    private static int sScanCmdId = 0;
+    private static ScanEventHandler sScanEventHandler;
+    private static ScanSettings sScanSettings;
+
+    synchronized public static boolean startScan(
+            ScanSettings settings, ScanEventHandler eventHandler) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+
+                if (sScanCmdId != 0) {
+                    stopScan();
+                } else if (sScanSettings != null || sScanEventHandler != null) {
+                /* current scan is paused; no need to stop it */
+                }
+
+                sScanCmdId = getNewCmdIdLocked();
+
+                sScanSettings = settings;
+                sScanEventHandler = eventHandler;
+
+                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
+                    sScanEventHandler = null;
+                    sScanSettings = null;
+                    sScanCmdId = 0;
+                    return false;
+                }
+
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    synchronized public static void stopScan() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                stopScanNative(sWlan0Index, sScanCmdId);
+                sScanSettings = null;
+                sScanEventHandler = null;
+                sScanCmdId = 0;
+            }
+        }
+    }
+
+    synchronized public static void pauseScan() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
+                    Log.d(TAG, "Pausing scan");
+                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
+                    stopScanNative(sWlan0Index, sScanCmdId);
+                    sScanCmdId = 0;
+                    sScanEventHandler.onScanPaused(scanData);
+                }
+            }
+        }
+    }
+
+    synchronized public static void restartScan() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
+                    Log.d(TAG, "Restarting scan");
+                    ScanEventHandler handler = sScanEventHandler;
+                    ScanSettings settings = sScanSettings;
+                    if (startScan(sScanSettings, sScanEventHandler)) {
+                        sScanEventHandler.onScanRestarted();
+                    } else {
+                    /* we are still paused; don't change state */
+                        sScanEventHandler = handler;
+                        sScanSettings = settings;
+                    }
+                }
+            }
+        }
+    }
+
+    synchronized public static WifiScanner.ScanData[] getScanResults(boolean flush) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getScanResultsNative(sWlan0Index, flush);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public static interface HotlistEventHandler {
+        void onHotlistApFound (ScanResult[] result);
+        void onHotlistApLost  (ScanResult[] result);
+    }
+
+    private static int sHotlistCmdId = 0;
+    private static HotlistEventHandler sHotlistEventHandler;
+
+    private native static boolean setHotlistNative(int iface, int id,
+            WifiScanner.HotlistSettings settings);
+    private native static boolean resetHotlistNative(int iface, int id);
+
+    synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
+                                    HotlistEventHandler eventHandler) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sHotlistCmdId != 0) {
+                    return false;
+                } else {
+                    sHotlistCmdId = getNewCmdIdLocked();
+                }
+
+                sHotlistEventHandler = eventHandler;
+                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
+                    sHotlistEventHandler = null;
+                    return false;
+                }
+
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    synchronized public static void resetHotlist() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sHotlistCmdId != 0) {
+                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
+                    sHotlistCmdId = 0;
+                    sHotlistEventHandler = null;
+                }
+            }
+        }
+    }
+
+    synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sHotlistCmdId != 0) {
+                    sHotlistEventHandler.onHotlistApFound(results);
+                } else {
+                /* this can happen because of race conditions */
+                    Log.d(TAG, "Ignoring hotlist AP found event");
+                }
+            }
+        }
+    }
+
+    synchronized public static void onHotlistApLost(int id, ScanResult[] results) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sHotlistCmdId != 0) {
+                    sHotlistEventHandler.onHotlistApLost(results);
+                } else {
+                /* this can happen because of race conditions */
+                    Log.d(TAG, "Ignoring hotlist AP lost event");
+                }
+            }
+        }
+    }
+
+    public static interface SignificantWifiChangeEventHandler {
+        void onChangesFound(ScanResult[] result);
+    }
+
+    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
+    private static int sSignificantWifiChangeCmdId;
+
+    private static native boolean trackSignificantWifiChangeNative(
+            int iface, int id, WifiScanner.WifiChangeSettings settings);
+    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
+
+    synchronized public static boolean trackSignificantWifiChange(
+            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sSignificantWifiChangeCmdId != 0) {
+                    return false;
+                } else {
+                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
+                }
+
+                sSignificantWifiChangeHandler = handler;
+                if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
+                    sSignificantWifiChangeHandler = null;
+                    return false;
+                }
+
+                return true;
+            } else {
+                return false;
+            }
+
+        }
+    }
+
+    synchronized static void untrackSignificantWifiChange() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sSignificantWifiChangeCmdId != 0) {
+                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
+                    sSignificantWifiChangeCmdId = 0;
+                    sSignificantWifiChangeHandler = null;
+                }
+            }
+        }
+    }
+
+    synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
+        synchronized (mLock) {
+            if (sSignificantWifiChangeCmdId != 0) {
+                sSignificantWifiChangeHandler.onChangesFound(results);
+            } else {
+            /* this can happen because of race conditions */
+                Log.d(TAG, "Ignoring significant wifi change");
+            }
+        }
+    }
+
+    synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
+        // TODO: use correct iface name to Index translation
+        if (iface == null) return null;
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getWifiLinkLayerStatsNative(sWlan0Index);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    synchronized public static void setWifiLinkLayerStats(String iface, int enable) {
+        if (iface == null) return;
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                setWifiLinkLayerStatsNative(sWlan0Index, enable);
+            }
+        }
+    }
+
     public static native int getSupportedFeatureSetNative(int iface);
     synchronized public static int getSupportedFeatureSet() {
-        return getSupportedFeatureSetNative(sWlan0Index);
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getSupportedFeatureSetNative(sWlan0Index);
+            } else {
+                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
+                return 0;
+            }
+        }
     }
 
     /* Rtt related commands/events */
@@ -1553,7 +1833,7 @@
             sRttEventHandler.onRttResults(results);
             sRttCmdId = 0;
         } else {
-            Log.d(TAG, "Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
+            Log.d(TAG, "RTT Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
         }
     }
 
@@ -1565,25 +1845,39 @@
     synchronized public static boolean requestRtt(
             RttManager.RttParams[] params, RttEventHandler handler) {
         synchronized (mLock) {
-            if (sRttCmdId != 0) {
-                return false;
+            if (isHalStarted()) {
+                if (sRttCmdId != 0) {
+                    Log.v("TAG", "Last one is still under measurement!");
+                    return false;
+                } else {
+                    sRttCmdId = getNewCmdIdLocked();
+                }
+                sRttEventHandler = handler;
+                Log.v(TAG, "native issue RTT request");
+                return requestRangeNative(sWlan0Index, sRttCmdId, params);
             } else {
-                sRttCmdId = getNewCmdIdLocked();
+                return false;
             }
-            sRttEventHandler = handler;
-            return requestRangeNative(sWlan0Index, sRttCmdId, params);
         }
     }
 
     synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
         synchronized(mLock) {
-            if (sRttCmdId == 0) {
-                return false;
-            }
+            if (isHalStarted()) {
+                if (sRttCmdId == 0) {
+                    return false;
+                }
 
-            if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
-                sRttEventHandler = null;
-                return true;
+                sRttCmdId = 0;
+
+                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
+                    sRttEventHandler = null;
+                    Log.v(TAG, "RTT cancel Request Successfully");
+                    return true;
+                } else {
+                    Log.e(TAG, "RTT cancel Request failed");
+                    return false;
+                }
             } else {
                 return false;
             }
@@ -1594,7 +1888,7 @@
 
     synchronized public static boolean setScanningMacOui(byte[] oui) {
         synchronized (mLock) {
-            if (startHal()) {
+            if (isHalStarted()) {
                 return setScanningMacOuiNative(sWlan0Index, oui);
             } else {
                 return false;
@@ -1607,11 +1901,494 @@
 
     synchronized public static int [] getChannelsForBand(int band) {
         synchronized (mLock) {
-            if (startHal()) {
+            if (isHalStarted()) {
                 return getChannelsForBandNative(sWlan0Index, band);
+	    } else {
+                return null;
+            }
+        }
+    }
+
+    private static native boolean isGetChannelsForBandSupportedNative();
+    synchronized public static boolean isGetChannelsForBandSupported(){
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return isGetChannelsForBandSupportedNative();
+	    } else {
+                return false;
+            }
+        }
+    }
+
+    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
+    synchronized public static boolean setDfsFlag(boolean dfsOn) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return setDfsFlagNative(sWlan0Index, dfsOn);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private static native boolean toggleInterfaceNative(int on);
+    synchronized public static boolean toggleInterface(int on) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return toggleInterfaceNative(0);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
+    synchronized public static RttManager.RttCapabilities getRttCapabilities() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getRttCapabilitiesNative(sWlan0Index);
+            }else {
+                return null;
+            }
+        }
+    }
+
+    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
+    synchronized public static boolean setCountryCodeHal( String CountryCode) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return setCountryCodeHalNative(sWlan0Index, CountryCode);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /* Rtt related commands/events */
+    public abstract class TdlsEventHandler {
+        abstract public void onTdlsStatus(String macAddr, int status, int reason);
+    }
+
+    private static TdlsEventHandler sTdlsEventHandler;
+
+    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
+            String macAddr);
+    synchronized public static boolean enableDisableTdls(boolean enable, String macAdd,
+            TdlsEventHandler tdlsCallBack) {
+        synchronized (mLock) {
+            sTdlsEventHandler = tdlsCallBack;
+            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
+        }
+    }
+
+    // Once TDLS per mac and event feature is implemented, this class definition should be
+    // moved to the right place, like WifiManager etc
+    public static class TdlsStatus {
+        int channel;
+        int global_operating_class;
+        int state;
+        int reason;
+    }
+    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
+    synchronized public static TdlsStatus getTdlsStatus (String macAdd) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getTdlsStatusNative(sWlan0Index, macAdd);
             } else {
                 return null;
             }
         }
     }
+
+    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
+    // moved to the right place, like WifiStateMachine etc
+    public static class TdlsCapabilities {
+        /* Maximum TDLS session number can be supported by the Firmware and hardware */
+        int maxConcurrentTdlsSessionNumber;
+        boolean isGlobalTdlsSupported;
+        boolean isPerMacTdlsSupported;
+        boolean isOffChannelTdlsSupported;
+    }
+
+
+
+    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
+    synchronized public static TdlsCapabilities getTdlsCapabilities () {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getTdlsCapabilitiesNative(sWlan0Index);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    synchronized private static boolean onTdlsStatus(String macAddr, int status, int reason) {
+         if (sTdlsEventHandler == null) {
+             return false;
+         } else {
+             sTdlsEventHandler.onTdlsStatus(macAddr, status, reason);
+             return true;
+         }
+    }
+
+    //---------------------------------------------------------------------------------
+
+    /* Wifi Logger commands/events */
+
+    public static native boolean startLogging(int iface);
+
+    public static interface WifiLoggerEventHandler {
+        void onRingBufferData(RingBufferStatus status, byte[] buffer);
+        void onWifiAlert(int errorCode, byte[] buffer);
+    }
+
+    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
+
+    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
+        if (sWifiLoggerEventHandler != null)
+            sWifiLoggerEventHandler.onRingBufferData(status, buffer);
+    }
+
+    private static void onWifiAlert(byte[] buffer, int errorCode) {
+        if (sWifiLoggerEventHandler != null)
+            sWifiLoggerEventHandler.onWifiAlert(errorCode, buffer);
+    }
+
+    private static int sLogCmdId = -1;
+    private static native boolean setLoggingEventHandlerNative(int iface, int id);
+    synchronized public static boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                int oldId =  sLogCmdId;
+                sLogCmdId = getNewCmdIdLocked();
+                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
+                    sLogCmdId = oldId;
+                    return false;
+                }
+                sWifiLoggerEventHandler = handler;
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
+            int flags, int minIntervalSec ,int minDataSize, String ringName);
+    synchronized public static boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
+            int minDataSize, String ringName){
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
+                        minDataSize, ringName);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private static native int getSupportedLoggerFeatureSetNative(int iface);
+    synchronized public static int getSupportedLoggerFeatureSet() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getSupportedLoggerFeatureSetNative(sWlan0Index);
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private static native boolean resetLogHandlerNative(int iface, int id);
+    synchronized public static boolean resetLogHandler() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if (sLogCmdId == -1) {
+                    Log.e(TAG,"Can not reset handler Before set any handler");
+                    return false;
+                }
+                sWifiLoggerEventHandler = null;
+                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
+                    sLogCmdId = -1;
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private static native String getDriverVersionNative(int iface);
+    synchronized public static String getDriverVersion() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getDriverVersionNative(sWlan0Index);
+            } else {
+                return "";
+            }
+        }
+    }
+
+
+    private static native String getFirmwareVersionNative(int iface);
+    synchronized public static String getFirmwareVersion() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getFirmwareVersionNative(sWlan0Index);
+            } else {
+                return "";
+            }
+        }
+    }
+
+    public static class RingBufferStatus{
+        String name;
+        int flag;
+        int ringBufferId;
+        int ringBufferByteSize;
+        int verboseLevel;
+        int writtenBytes;
+        int readBytes;
+        int writtenRecords;
+
+        @Override
+        public String toString() {
+            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
+                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
+                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
+                    " writtenRecords: " + writtenRecords;
+        }
+    }
+
+    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
+    synchronized public static RingBufferStatus[] getRingBufferStatus() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getRingBufferStatusNative(sWlan0Index);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private static native boolean getRingBufferDataNative(int iface, String ringName);
+    synchronized public static boolean getRingBufferData(String ringName) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                return getRingBufferDataNative(sWlan0Index, ringName);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    static private byte[] mFwMemoryDump;
+    private static void onWifiFwMemoryAvailable(byte[] buffer) {
+        mFwMemoryDump = buffer;
+        if (DBG) {
+            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
+                    (buffer == null ? 0 :  buffer.length));
+        }
+    }
+
+    private static native boolean getFwMemoryDumpNative(int iface);
+    synchronized public static byte[] getFwMemoryDump() {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                if(getFwMemoryDumpNative(sWlan0Index)) {
+                    byte[] fwMemoryDump = mFwMemoryDump;
+                    mFwMemoryDump = null;
+                    return fwMemoryDump;
+                } else {
+                    return null;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    //---------------------------------------------------------------------------------
+    /* Configure ePNO */
+
+    public class WifiPnoNetwork {
+        String SSID;
+        int rssi_threshold;
+        int flags;
+        int auth;
+        String configKey; // kept for reference
+
+        WifiPnoNetwork(WifiConfiguration config, int threshold) {
+            if (config.SSID == null) {
+                this.SSID = "";
+                this.flags = 1;
+            } else {
+                this.SSID = config.SSID;
+            }
+            this.rssi_threshold = threshold;
+            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+                auth |= 2;
+            } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
+                    config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+                auth |= 4;
+            } else if (config.wepKeys[0] != null) {
+                auth |= 1;
+            } else {
+                auth |= 1;
+            }
+//            auth = 0;
+            flags |= 6; //A and G
+            configKey = config.configKey();
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sbuf = new StringBuilder();
+            sbuf.append(this.SSID);
+            sbuf.append(" flags=").append(this.flags);
+            sbuf.append(" rssi=").append(this.rssi_threshold);
+            sbuf.append(" auth=").append(this.auth);
+            return sbuf.toString();
+        }
+    }
+
+    public static interface WifiPnoEventHandler {
+        void onPnoNetworkFound(ScanResult results[]);
+    }
+
+    private static WifiPnoEventHandler sWifiPnoEventHandler;
+
+    private static int sPnoCmdId = 0;
+
+    private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
+
+    synchronized public static boolean setPnoList(WifiPnoNetwork list[],
+                                                  WifiPnoEventHandler eventHandler) {
+        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
+
+        synchronized (mLock) {
+            if (isHalStarted()) {
+
+                sPnoCmdId = getNewCmdIdLocked();
+
+                sWifiPnoEventHandler = eventHandler;
+                if (setPnoListNative(sWlan0Index, sPnoCmdId, list)) {
+                    return true;
+                }
+            }
+
+            sWifiPnoEventHandler = null;
+            return false;
+        }
+    }
+
+    synchronized public static void onPnoNetworkFound(int id, ScanResult[] results) {
+
+        if (results == null) {
+            Log.e(TAG, "onPnoNetworkFound null results");
+            return;
+
+        }
+        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
+
+        //Log.e(TAG, "onPnoNetworkFound length " + results.length);
+        //return;
+        for (int i=0; i<results.length; i++) {
+            Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
+                    + " " + results[i].level + " " + results[i].frequency);
+
+            populateScanResult(results[i], results[i].bytes, "onPnoNetworkFound ");
+            results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
+        }
+        synchronized (mLock) {
+            if (sPnoCmdId != 0 && sWifiPnoEventHandler != null) {
+                sWifiPnoEventHandler.onPnoNetworkFound(results);
+            } else {
+                /* this can happen because of race conditions */
+                Log.d(TAG, "Ignoring Pno Network found event");
+            }
+        }
+    }
+
+    public class WifiLazyRoamParams {
+        int A_band_boost_threshold;
+        int A_band_penalty_threshold;
+        int A_band_boost_factor;
+        int A_band_penalty_factor;
+        int A_band_max_boost;
+        int lazy_roam_hysteresis;
+        int alert_roam_rssi_trigger;
+
+        WifiLazyRoamParams() {
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sbuf = new StringBuilder();
+            sbuf.append(" A_band_boost_threshold=").append(this.A_band_boost_threshold);
+            sbuf.append(" A_band_penalty_threshold=").append(this.A_band_penalty_threshold);
+            sbuf.append(" A_band_boost_factor=").append(this.A_band_boost_factor);
+            sbuf.append(" A_band_penalty_factor=").append(this.A_band_penalty_factor);
+            sbuf.append(" A_band_max_boost=").append(this.A_band_max_boost);
+            sbuf.append(" lazy_roam_hysteresis=").append(this.lazy_roam_hysteresis);
+            sbuf.append(" alert_roam_rssi_trigger=").append(this.alert_roam_rssi_trigger);
+            return sbuf.toString();
+        }
+    }
+
+    private native static boolean setLazyRoamNative(int iface, int id,
+                                              boolean enabled, WifiLazyRoamParams param);
+
+    synchronized public static boolean setLazyRoam(boolean enabled, WifiLazyRoamParams params) {
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                sPnoCmdId = getNewCmdIdLocked();
+                return setLazyRoamNative(sWlan0Index, sPnoCmdId, enabled, params);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private native static boolean setBssidBlacklistNative(int iface, int id,
+                                              String list[]);
+
+    synchronized public static boolean setBssidBlacklist(String list[]) {
+        int size = 0;
+        if (list != null) {
+            size = list.length;
+        }
+        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
+
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                sPnoCmdId = getNewCmdIdLocked();
+                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private native static boolean setSsidWhitelistNative(int iface, int id, String list[]);
+
+    synchronized public static boolean setSsidWhitelist(String list[]) {
+        int size = 0;
+        if (list != null) {
+            size = list.length;
+        }
+        Log.e(TAG, "setSsidWhitelist cmd " + sPnoCmdId + " size " + size);
+
+        synchronized (mLock) {
+            if (isHalStarted()) {
+                sPnoCmdId = getNewCmdIdLocked();
+
+                return setSsidWhitelistNative(sWlan0Index, sPnoCmdId, list);
+            } else {
+                return false;
+            }
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
index 0a7df0b..cf14c5f 100644
--- a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
+++ b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
@@ -139,7 +139,7 @@
     }
 
      private String buildNetworkKey(ScoredNetwork network) {
-        if (network.networkKey == null) return null;
+        if (network == null || network.networkKey == null) return null;
         if (network.networkKey.wifiKey == null) return null;
         if (network.networkKey.type == NetworkKey.TYPE_WIFI) {
             String key = network.networkKey.wifiKey.ssid;
@@ -153,7 +153,7 @@
     }
 
     private String buildNetworkKey(ScanResult result) {
-        if (result.SSID == null) {
+        if (result == null || result.SSID == null) {
             return null;
         }
         StringBuilder key = new StringBuilder("\"");
diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java
index 20d64c9..406a764 100644
--- a/service/java/com/android/server/wifi/WifiNotificationController.java
+++ b/service/java/com/android/server/wifi/WifiNotificationController.java
@@ -65,7 +65,7 @@
     /**
      * The Notification object given to the NotificationManager.
      */
-    private Notification mNotification;
+    private Notification.Builder mNotificationBuilder;
     /**
      * Whether the notification is being shown, as set by us. That is, if the
      * user cancels the notification, we will not receive the callback so this
@@ -229,31 +229,32 @@
                 return;
             }
 
-            if (mNotification == null) {
-                // Cache the Notification object.
-                mNotification = new Notification();
-                mNotification.when = 0;
-                mNotification.icon = ICON_NETWORKS_AVAILABLE;
-                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
-                mNotification.contentIntent = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(
-                                new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
-                        .getPendingIntent(0, 0, null, UserHandle.CURRENT);
+            if (mNotificationBuilder == null) {
+                // Cache the Notification builder object.
+                mNotificationBuilder = new Notification.Builder(mContext)
+                        .setWhen(0)
+                        .setSmallIcon(ICON_NETWORKS_AVAILABLE)
+                        .setAutoCancel(true)
+                        .setContentIntent(TaskStackBuilder.create(mContext)
+                                .addNextIntentWithParentStack(
+                                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
+                                .getPendingIntent(0, 0, null, UserHandle.CURRENT))
+                        .setColor(mContext.getResources().getColor(
+                                com.android.internal.R.color.system_notification_accent_color));
             }
 
             CharSequence title = mContext.getResources().getQuantityText(
                     com.android.internal.R.plurals.wifi_available, numNetworks);
             CharSequence details = mContext.getResources().getQuantityText(
                     com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
-            mNotification.tickerText = title;
-            mNotification.color = mContext.getResources().getColor(
-                    com.android.internal.R.color.system_notification_accent_color);
-            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+            mNotificationBuilder.setTicker(title);
+            mNotificationBuilder.setContentTitle(title);
+            mNotificationBuilder.setContentText(details);
 
             mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
 
-            notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
-                    UserHandle.ALL);
+            notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE,
+                    mNotificationBuilder.build(), UserHandle.ALL);
         } else {
             notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
         }
diff --git a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
index 6dacfb4..8f2cd3b 100644
--- a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
@@ -16,18 +16,24 @@
 
 package com.android.server.wifi;
 
+import android.Manifest;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.wifi.IWifiScanner;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiScanner.BssidInfo;
+import android.net.wifi.WifiScanner.ChannelSpec;
+import android.net.wifi.WifiScanner.ScanData;
 import android.net.wifi.WifiScanner.ScanSettings;
 import android.net.wifi.WifiSsid;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -36,13 +42,16 @@
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.LocalLog;
 import android.util.Log;
-import android.util.Slog;
 
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.State;
+import com.android.server.am.BatteryStatsService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -51,26 +60,49 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 public class WifiScanningServiceImpl extends IWifiScanner.Stub {
 
     private static final String TAG = "WifiScanningService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
     private static final int INVALID_KEY = 0;                               // same as WifiScanner
     private static final int MIN_PERIOD_PER_CHANNEL_MS = 200;               // DFS needs 120 ms
+    private static final int UNKNOWN_PID = -1;
+
+    private static final LocalLog mLocalLog = new LocalLog(1024);
+
+    private static void localLog(String message) {
+        mLocalLog.log(message);
+    }
+
+    private static void logw(String message) {
+        Log.w(TAG, message);
+        mLocalLog.log(message);
+    }
+
+    private static void loge(String message) {
+        Log.e(TAG, message);
+        mLocalLog.log(message);
+    }
 
     @Override
     public Messenger getMessenger() {
-        return new Messenger(mClientHandler);
+        if (mClientHandler != null) {
+            return new Messenger(mClientHandler);
+        } else {
+            loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
+            return null;
+        }
     }
 
     @Override
     public Bundle getAvailableChannels(int band) {
-        WifiScanner.ChannelSpec channelSpecs[] = getChannelsForBand(band);
+        ChannelSpec channelSpecs[] = getChannelsForBand(band);
         ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length);
-        for (WifiScanner.ChannelSpec channelSpec : channelSpecs) {
+        for (ChannelSpec channelSpec : channelSpecs) {
             list.add(channelSpec.frequency);
         }
         Bundle b = new Bundle();
@@ -78,10 +110,11 @@
         return b;
     }
 
-    private void enforceConnectivityInternalPermission() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.CONNECTIVITY_INTERNAL,
-                "WifiScanningServiceImpl");
+    private void enforceLocationHardwarePermission(int uid) {
+        mContext.enforcePermission(
+                Manifest.permission.LOCATION_HARDWARE,
+                UNKNOWN_PID, uid,
+                "LocationHardware");
     }
 
     private class ClientHandler extends Handler {
@@ -93,50 +126,53 @@
         @Override
         public void handleMessage(Message msg) {
 
-            if (DBG) Log.d(TAG, "ClientHandler got" + msg);
+            if (DBG) localLog("ClientHandler got" + msg);
 
             switch (msg.what) {
 
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        AsyncChannel c = (AsyncChannel) msg.obj;
-                        if (DBG) Slog.d(TAG, "New client listening to asynchronous messages: " +
-                                msg.replyTo);
-                        ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
-                        mClients.put(msg.replyTo, cInfo);
-                    } else {
-                        Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
-                    }
-                    return;
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
-                        Slog.e(TAG, "Send failed, client connection lost");
-                    } else {
-                        if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
-                    }
-                    if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
-                    ClientInfo ci = mClients.remove(msg.replyTo);
-                    if (ci != null) {                       /* can be null if send failed above */
-                        ci.cleanup();
+                    if (msg.arg1 != AsyncChannel.STATUS_SUCCESSFUL) {
+                        loge("Client connection failure, error=" + msg.arg1);
                     }
                     return;
                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, this, msg.replyTo);
+                    if (DBG) localLog("New client connected : " + msg.sendingUid + msg.replyTo);
+                    ClientInfo cInfo = new ClientInfo(msg.sendingUid, ac, msg.replyTo);
+                    mClients.put(msg.replyTo, cInfo);
+                    return;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+                        loge("Send failed, client connection lost");
+                    } else {
+                        if (DBG) localLog("Client connection lost with reason: " + msg.arg1);
+                    }
+                    if (DBG) localLog("closing client " + msg.replyTo);
+                    ClientInfo ci = mClients.remove(msg.replyTo);
+                    if (ci != null) {                       /* can be null if send failed above */
+                        if (DBG) localLog("closing client " + ci.mUid);
+                        ci.cleanup();
+                    }
                     return;
             }
 
-            ClientInfo ci = mClients.get(msg.replyTo);
-            if (ci == null) {
-                Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
-                replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
+            try {
+                enforceLocationHardwarePermission(msg.sendingUid);
+            } catch (SecurityException e) {
+                localLog("failed to authorize app: " + e);
+                replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
                 return;
             }
 
-            try {
-                enforceConnectivityInternalPermission();
-            } catch (SecurityException e) {
-                replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
+            if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
+                mStateMachine.sendMessage(Message.obtain(msg));
+                return;
+            }
+            ClientInfo ci = mClients.get(msg.replyTo);
+            if (ci == null) {
+                loge("Could not find client info for message " + msg.replyTo);
+                replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
                 return;
             }
 
@@ -144,6 +180,8 @@
                     WifiScanner.CMD_SCAN,
                     WifiScanner.CMD_START_BACKGROUND_SCAN,
                     WifiScanner.CMD_STOP_BACKGROUND_SCAN,
+                    WifiScanner.CMD_START_SINGLE_SCAN,
+                    WifiScanner.CMD_STOP_SINGLE_SCAN,
                     WifiScanner.CMD_SET_HOTLIST,
                     WifiScanner.CMD_RESET_HOTLIST,
                     WifiScanner.CMD_CONFIGURE_WIFI_CHANGE,
@@ -173,10 +211,13 @@
     private static final int CMD_DRIVER_UNLOADED                     = BASE + 7;
     private static final int CMD_SCAN_PAUSED                         = BASE + 8;
     private static final int CMD_SCAN_RESTARTED                      = BASE + 9;
+    private static final int CMD_STOP_SCAN_INTERNAL                  = BASE + 10;
 
     private Context mContext;
     private WifiScanningStateMachine mStateMachine;
     private ClientHandler mClientHandler;
+    private IBatteryStats mBatteryStats;
+    private final WifiNative.ScanCapabilities mScanCapabilities = new WifiNative.ScanCapabilities();
 
     WifiScanningServiceImpl() { }
 
@@ -193,6 +234,7 @@
         mClientHandler = new ClientHandler(thread.getLooper());
         mStateMachine = new WifiScanningStateMachine(thread.getLooper());
         mWifiChangeStateMachine = new WifiChangeStateMachine(thread.getLooper());
+        mBatteryStats = BatteryStatsService.getService();
 
         mContext.registerReceiver(
                 new BroadcastReceiver() {
@@ -200,7 +242,7 @@
                     public void onReceive(Context context, Intent intent) {
                         int state = intent.getIntExtra(
                                 WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
-                        if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state);
+                        if (DBG) localLog("SCAN_AVAILABLE : " + state);
                         if (state == WifiManager.WIFI_STATE_ENABLED) {
                             mStateMachine.sendMessage(CMD_DRIVER_LOADED);
                         } else if (state == WifiManager.WIFI_STATE_DISABLED) {
@@ -236,62 +278,67 @@
 
         @Override
         public void onScanResultsAvailable() {
-            if (DBG) Log.d(TAG, "onScanResultAvailable event received");
+            if (DBG) localLog("onScanResultAvailable event received");
             sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
         }
 
         @Override
-        public void onSingleScanComplete() {
-            if (DBG) Log.d(TAG, "onSingleScanComplete event received");
+        public void onScanStatus() {
+            if (DBG) localLog("onScanStatus event received");
             sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
         }
 
         @Override
         public void onFullScanResult(ScanResult fullScanResult) {
-            if (DBG) Log.d(TAG, "Full scanresult received");
+            if (DBG) localLog("Full scanresult received");
             sendMessage(CMD_FULL_SCAN_RESULTS, 0, 0, fullScanResult);
         }
 
         @Override
-        public void onScanPaused() {
-            sendMessage(CMD_SCAN_PAUSED);
+        public void onScanPaused(ScanData scanData[]) {
+            sendMessage(CMD_SCAN_PAUSED, scanData);
         }
 
         @Override
         public void onScanRestarted() {
+            if (DBG) localLog("onScanRestarted() event received");
             sendMessage(CMD_SCAN_RESTARTED);
         }
 
         @Override
         public void onHotlistApFound(ScanResult[] results) {
-            if (DBG) Log.d(TAG, "HotlistApFound event received");
+            if (DBG) localLog("HotlistApFound event received");
             sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
         }
 
         @Override
+        public void onHotlistApLost(ScanResult[] results) {
+            if (DBG) localLog("HotlistApLost event received");
+            sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results);
+        }
+
+        @Override
         public void onChangesFound(ScanResult[] results) {
-            if (DBG) Log.d(TAG, "onWifiChangesFound event received");
+            if (DBG) localLog("onWifiChangesFound event received");
             sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
         }
 
         class DefaultState extends State {
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "DefaultState");
+                if (DBG) localLog("DefaultState");
             }
             @Override
             public boolean processMessage(Message msg) {
 
-                if (DBG) Log.d(TAG, "DefaultState got" + msg);
+                if (DBG) localLog("DefaultState got" + msg);
 
                 ClientInfo ci = mClients.get(msg.replyTo);
 
                 switch (msg.what) {
                     case CMD_DRIVER_LOADED:
-                        if (WifiNative.startHal() && WifiNative.getInterfaces() != 0) {
-                            WifiNative.ScanCapabilities capabilities =
-                                    new WifiNative.ScanCapabilities();
-                            if (WifiNative.getScanCapabilities(capabilities)) {
+                        if (WifiNative.getInterfaces() != 0) {
+                            if (WifiNative.getScanCapabilities(mScanCapabilities)) {
                                 transitionTo(mStartedState);
                             } else {
                                 loge("could not get scan capabilities");
@@ -303,20 +350,23 @@
                     case WifiScanner.CMD_SCAN:
                     case WifiScanner.CMD_START_BACKGROUND_SCAN:
                     case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
+                    case WifiScanner.CMD_START_SINGLE_SCAN:
+                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
                     case WifiScanner.CMD_SET_HOTLIST:
                     case WifiScanner.CMD_RESET_HOTLIST:
                     case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
                     case WifiScanner.CMD_START_TRACKING_CHANGE:
                     case WifiScanner.CMD_STOP_TRACKING_CHANGE:
+                    case WifiScanner.CMD_GET_SCAN_RESULTS:
                         replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
                         break;
 
                     case CMD_SCAN_RESULTS_AVAILABLE:
-                        if (DBG) log("ignored scan results available event");
+                        if (DBG) localLog("ignored scan results available event");
                         break;
 
                     case CMD_FULL_SCAN_RESULTS:
-                        if (DBG) log("ignored full scan result event");
+                        if (DBG) localLog("ignored full scan result event");
                         break;
 
                     default:
@@ -331,13 +381,13 @@
 
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "StartedState");
+                if (DBG) localLog("StartedState");
             }
 
             @Override
             public boolean processMessage(Message msg) {
 
-                if (DBG) Log.d(TAG, "StartedState got" + msg);
+                if (DBG) localLog("StartedState got" + msg);
 
                 ClientInfo ci = mClients.get(msg.replyTo);
 
@@ -350,7 +400,7 @@
                         break;
                     case WifiScanner.CMD_START_BACKGROUND_SCAN:
                         if (addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
-                            replySucceeded(msg, null);
+                            replySucceeded(msg);
                         } else {
                             replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
                         }
@@ -359,18 +409,33 @@
                         removeScanRequest(ci, msg.arg2);
                         break;
                     case WifiScanner.CMD_GET_SCAN_RESULTS:
-                        replySucceeded(msg, getScanResults(ci));
+                        reportScanResults();
+                        replySucceeded(msg);
+                        break;
+                    case WifiScanner.CMD_START_SINGLE_SCAN:
+                        if (addSingleScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
+                            replySucceeded(msg);
+                        } else {
+                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
+                        }
+                        break;
+                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
+                        removeScanRequest(ci, msg.arg2);
+                        break;
+                    case CMD_STOP_SCAN_INTERNAL:
+                        localLog("Removing single shot scan");
+                        removeScanRequest((ClientInfo) msg.obj, msg.arg2);
                         break;
                     case WifiScanner.CMD_SET_HOTLIST:
                         setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj);
-                        replySucceeded(msg, null);
+                        replySucceeded(msg);
                         break;
                     case WifiScanner.CMD_RESET_HOTLIST:
                         resetHotlist(ci, msg.arg2);
                         break;
                     case WifiScanner.CMD_START_TRACKING_CHANGE:
                         trackWifiChanges(ci, msg.arg2);
-                        replySucceeded(msg, null);
+                        replySucceeded(msg);
                         break;
                     case WifiScanner.CMD_STOP_TRACKING_CHANGE:
                         untrackWifiChanges(ci, msg.arg2);
@@ -379,7 +444,7 @@
                         configureWifiChange((WifiScanner.WifiChangeSettings) msg.obj);
                         break;
                     case CMD_SCAN_RESULTS_AVAILABLE: {
-                            ScanResult[] results = WifiNative.getScanResults();
+                            ScanData[] results = WifiNative.getScanResults(/* flush = */ true);
                             Collection<ClientInfo> clients = mClients.values();
                             for (ClientInfo ci2 : clients) {
                                 ci2.reportScanResults(results);
@@ -388,7 +453,7 @@
                         break;
                     case CMD_FULL_SCAN_RESULTS: {
                             ScanResult result = (ScanResult) msg.obj;
-                            if (DBG) Log.d(TAG, "reporting fullscan result for " + result.SSID);
+                            if (DBG) localLog("reporting fullscan result for " + result.SSID);
                             Collection<ClientInfo> clients = mClients.values();
                             for (ClientInfo ci2 : clients) {
                                 ci2.reportFullScanResult(result);
@@ -398,10 +463,19 @@
 
                     case CMD_HOTLIST_AP_FOUND: {
                             ScanResult[] results = (ScanResult[])msg.obj;
-                            if (DBG) Log.d(TAG, "Found " + results.length + " results");
+                            if (DBG) localLog("Found " + results.length + " results");
                             Collection<ClientInfo> clients = mClients.values();
                             for (ClientInfo ci2 : clients) {
-                                ci2.reportHotlistResults(results);
+                                ci2.reportHotlistResults(WifiScanner.CMD_AP_FOUND, results);
+                            }
+                        }
+                        break;
+                    case CMD_HOTLIST_AP_LOST: {
+                            ScanResult[] results = (ScanResult[])msg.obj;
+                            if (DBG) localLog("Lost " + results.length + " results");
+                            Collection<ClientInfo> clients = mClients.values();
+                            for (ClientInfo ci2 : clients) {
+                                ci2.reportHotlistResults(WifiScanner.CMD_AP_LOST, results);
                             }
                         }
                         break;
@@ -415,7 +489,15 @@
                             reportWifiStabilized(results);
                         }
                         break;
-
+                    case CMD_SCAN_PAUSED: {
+                            ScanData results[] = (ScanData[]) msg.obj;
+                            Collection<ClientInfo> clients = mClients.values();
+                            for (ClientInfo ci2 : clients) {
+                                ci2.reportScanResults(results);
+                            }
+                            transitionTo(mPausedState);
+                        }
+                        break;
                     default:
                         return NOT_HANDLED;
                 }
@@ -427,13 +509,13 @@
         class PausedState extends State {
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "PausedState");
+                if (DBG) localLog("PausedState");
             }
 
             @Override
             public boolean processMessage(Message msg) {
 
-                if (DBG) Log.d(TAG, "PausedState got" + msg);
+                if (DBG) localLog("PausedState got" + msg);
 
                 switch (msg.what) {
                     case CMD_SCAN_RESTARTED:
@@ -450,11 +532,17 @@
 
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            super.dump(fd, pw, args);
             pw.println("number of clients : " + mClients.size());
+            for (ClientInfo client : mClients.values()) {
+                client.dump(fd, pw, args);
+                pw.append("------\n");
+            }
             pw.println();
+            pw.println("localLog : ");
+            mLocalLog.dump(fd, pw, args);
+            pw.append("\n\n");
+            super.dump(fd, pw, args);
         }
-
     }
 
     /* client management */
@@ -464,28 +552,93 @@
         private static final int MAX_LIMIT = 16;
         private final AsyncChannel mChannel;
         private final Messenger mMessenger;
+        private final int mUid;
+        private final WorkSource mWorkSource;
+        private boolean mScanWorkReported = false;
 
-        ClientInfo(AsyncChannel c, Messenger m) {
+        ClientInfo(int uid, AsyncChannel c, Messenger m) {
             mChannel = c;
             mMessenger = m;
-            if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
+            mUid = uid;
+            mWorkSource = new WorkSource(uid, TAG);
+            if (DBG) localLog("New client, channel: " + c + " messenger: " + m);
+        }
+
+        void reportBatchedScanStart() {
+            if (mUid == 0)
+                return;
+
+            int csph = getCsph();
+
+            try {
+                mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
+                localLog("started scanning for UID " + mUid + ", csph = " + csph);
+            } catch (RemoteException e) {
+                logw("failed to report scan work: " + e.toString());
+            }
+        }
+
+        void reportBatchedScanStop() {
+            if (mUid == 0)
+                return;
+
+            try {
+                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
+                localLog("stopped scanning for UID " + mUid);
+            } catch (RemoteException e) {
+                logw("failed to cleanup scan work: " + e.toString());
+            }
+        }
+
+        int getCsph() {
+            int csph = 0;
+            for (ScanSettings settings : getScanSettings()) {
+                int num_channels = settings.channels == null ? 0 : settings.channels.length;
+                if (num_channels == 0 && settings.band != 0) {
+                    num_channels = getChannelsForBand(settings.band).length;
+                }
+
+                int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs;
+                csph += num_channels * scans_per_Hour;
+            }
+
+            return csph;
+        }
+
+        void reportScanWorkUpdate() {
+            if (mScanWorkReported) {
+                reportBatchedScanStop();
+                mScanWorkReported = false;
+            }
+            if (mScanSettings.isEmpty() == false) {
+                reportBatchedScanStart();
+                mScanWorkReported = true;
+            }
         }
 
         @Override
         public String toString() {
             StringBuffer sb = new StringBuffer();
             sb.append("mChannel ").append(mChannel).append("\n");
-            sb.append("mMessenger ").append(mMessenger).append("\n");
+            sb.append("mMessenger ").append(mMessenger);
+            return sb.toString();
+        }
+
+        void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(toString());
 
             Iterator<Map.Entry<Integer, ScanSettings>> it = mScanSettings.entrySet().iterator();
             for (; it.hasNext(); ) {
                 Map.Entry<Integer, ScanSettings> entry = it.next();
-                sb.append("[ScanId ").append(entry.getKey()).append("\n");
-                sb.append("ScanSettings ").append(entry.getValue()).append("\n");
-                sb.append("]");
+                sb.append("ScanId ").append(entry.getKey()).append("\n");
+
+                ScanSettings scanSettings = entry.getValue();
+                sb.append(describe(scanSettings));
+                sb.append("\n");
             }
 
-            return sb.toString();
+            pw.println(sb.toString());
         }
 
         HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4);
@@ -493,13 +646,19 @@
 
         void addScanRequest(ScanSettings settings, int id) {
             mScanSettings.put(id, settings);
+            reportScanWorkUpdate();
         }
 
         void removeScanRequest(int id) {
-            mScanSettings.remove(id);
+            ScanSettings settings = mScanSettings.remove(id);
+            if (settings != null && settings.periodInMs == 0) {
+                /* this was a single shot scan */
+                mChannel.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, id);
+            }
+            reportScanWorkUpdate();
         }
 
-        Iterator<Map.Entry<Integer, WifiScanner.ScanSettings>> getScans() {
+        Iterator<Map.Entry<Integer, ScanSettings>> getScans() {
             return mScanSettings.entrySet().iterator();
         }
 
@@ -507,7 +666,7 @@
             return mScanSettings.values();
         }
 
-        void reportScanResults(ScanResult[] results) {
+        void reportScanResults(ScanData[] results) {
             Iterator<Integer> it = mScanSettings.keySet().iterator();
             while (it.hasNext()) {
                 int handler = it.next();
@@ -515,9 +674,9 @@
             }
         }
 
-        void reportScanResults(ScanResult[] results, int handler) {
+        void reportScanResults(ScanData[] results, int handler) {
             ScanSettings settings = mScanSettings.get(handler);
-            WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+            ChannelSpec desiredChannels[] = settings.channels;
             if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
                     || desiredChannels == null || desiredChannels.length == 0)  {
                 desiredChannels = getChannelsForBand(settings.band);
@@ -525,42 +684,62 @@
 
             // check the channels this client asked for ..
             int num_results = 0;
-            for (ScanResult result : results) {
-                for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
-                    if (channelSpec.frequency == result.frequency) {
+            for (ScanData result : results) {
+                boolean copyScanData = false;
+                for (ScanResult scanResult : result.getResults()) {
+                    for (ChannelSpec channelSpec : desiredChannels) {
+                        if (channelSpec.frequency == scanResult.frequency) {
+                            copyScanData = true;
+                            break;
+                        }
+                    }
+                    if (copyScanData) {
                         num_results++;
                         break;
                     }
                 }
             }
 
-            if (num_results == 0) {
-                // nothing to report
-                return;
-            }
+            localLog("results = " + results.length + ", num_results = " + num_results);
 
-            ScanResult results2[] = new ScanResult[num_results];
+            ScanData results2[] = new ScanData[num_results];
             int index = 0;
-            for (ScanResult result : results) {
-                for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
-                    if (channelSpec.frequency == result.frequency) {
-                        WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
-                        ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "",
-                                result.level, result.frequency, result.timestamp);
-                        results2[index] = newResult;
-                        index++;
+            for (ScanData result : results) {
+                boolean copyScanData = false;
+                for (ScanResult scanResult : result.getResults()) {
+                    for (ChannelSpec channelSpec : desiredChannels) {
+                        if (channelSpec.frequency == scanResult.frequency) {
+                            copyScanData = true;
+                            break;
+                        }
+                    }
+                    if (copyScanData) {
                         break;
                     }
                 }
+
+                if (copyScanData) {
+                    if (VDBG) {
+                        localLog("adding at " + index);
+                    }
+                    results2[index] = new WifiScanner.ScanData(result);
+                    index++;
+                }
             }
+            
+            localLog("delivering results, num = " + results2.length);
 
             deliverScanResults(handler, results2);
+            if (settings.periodInMs == 0) {
+                /* this is a single shot scan; stop the scan now */
+                mStateMachine.sendMessage(CMD_STOP_SCAN_INTERNAL, 0, handler, this);
+            }
         }
 
-        void deliverScanResults(int handler, ScanResult results[]) {
-            WifiScanner.ParcelableScanResults parcelableScanResults =
-                    new WifiScanner.ParcelableScanResults(results);
-            mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanResults);
+        void deliverScanResults(int handler, ScanData results[]) {
+            WifiScanner.ParcelableScanData parcelableScanData =
+                    new WifiScanner.ParcelableScanData(results);
+            mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
         }
 
         void reportFullScanResult(ScanResult result) {
@@ -568,17 +747,15 @@
             while (it.hasNext()) {
                 int handler = it.next();
                 ScanSettings settings = mScanSettings.get(handler);
-                WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+                ChannelSpec desiredChannels[] = settings.channels;
                 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
                         || desiredChannels == null || desiredChannels.length == 0)  {
                     desiredChannels = getChannelsForBand(settings.band);
                 }
-                for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
+                for (ChannelSpec channelSpec : desiredChannels) {
                     if (channelSpec.frequency == result.frequency) {
-                        WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
-                        ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "",
-                                result.level, result.frequency, result.timestamp);
-                        if (DBG) Log.d(TAG, "sending it to " + handler);
+                        ScanResult newResult = new ScanResult(result);
+                        if (DBG) localLog("sending it to " + handler);
                         newResult.informationElements = result.informationElements.clone();
                         mChannel.sendMessage(
                                 WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
@@ -614,7 +791,7 @@
             return mHotlistSettings.values();
         }
 
-        void reportHotlistResults(ScanResult[] results) {
+        void reportHotlistResults(int what, ScanResult[] results) {
             Iterator<Map.Entry<Integer, WifiScanner.HotlistSettings>> it =
                     mHotlistSettings.entrySet().iterator();
             while (it.hasNext()) {
@@ -623,7 +800,7 @@
                 WifiScanner.HotlistSettings settings = entry.getValue();
                 int num_results = 0;
                 for (ScanResult result : results) {
-                    for (WifiScanner.BssidInfo BssidInfo : settings.bssidInfos) {
+                    for (BssidInfo BssidInfo : settings.bssidInfos) {
                         if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
                             num_results++;
                             break;
@@ -639,7 +816,7 @@
                 ScanResult results2[] = new ScanResult[num_results];
                 int index = 0;
                 for (ScanResult result : results) {
-                    for (WifiScanner.BssidInfo BssidInfo : settings.bssidInfos) {
+                    for (BssidInfo BssidInfo : settings.bssidInfos) {
                         if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
                             results2[index] = result;
                             index++;
@@ -650,7 +827,7 @@
                 WifiScanner.ParcelableScanResults parcelableScanResults =
                         new WifiScanner.ParcelableScanResults(results2);
 
-                mChannel.sendMessage(WifiScanner.CMD_AP_FOUND, 0, handler, parcelableScanResults);
+                mChannel.sendMessage(what, 0, handler, parcelableScanResults);
             }
         }
 
@@ -701,16 +878,15 @@
             }
 
             mSignificantWifiHandlers.clear();
-            Log.d(TAG, "Successfully stopped all requests for client " + this);
+            localLog("Successfully stopped all requests for client " + this);
         }
     }
 
-    void replySucceeded(Message msg, Object obj) {
+    void replySucceeded(Message msg) {
         if (msg.replyTo != null) {
             Message reply = Message.obtain();
             reply.what = WifiScanner.CMD_OP_SUCCEEDED;
             reply.arg2 = msg.arg2;
-            reply.obj = obj;
             try {
                 msg.replyTo.send(reply);
             } catch (RemoteException e) {
@@ -737,9 +913,9 @@
         }
     }
 
-    private static class SettingsComputer {
+    private class SettingsComputer {
 
-        private static class TimeBucket {
+        private class TimeBucket {
             int periodInSecond;
             int periodMinInSecond;
             int periodMaxInSecond;
@@ -751,53 +927,49 @@
             }
         }
 
-        private static final TimeBucket[] mTimeBuckets = new TimeBucket[] {
+        private final TimeBucket[] mTimeBuckets = new TimeBucket[] {
                 new TimeBucket( 1, 0, 5 ),
                 new TimeBucket( 5, 5, 10 ),
                 new TimeBucket( 10, 10, 25 ),
                 new TimeBucket( 30, 25, 55 ),
-                new TimeBucket( 60, 55, 100),
+                new TimeBucket( 60, 55, 240),
                 new TimeBucket( 300, 240, 500),
                 new TimeBucket( 600, 500, 1500),
                 new TimeBucket( 1800, 1500, WifiScanner.MAX_SCAN_PERIOD_MS) };
 
-        private static final int MAX_BUCKETS = 8;
-        private static final int MAX_CHANNELS = 8;
-        private static final int DEFAULT_MAX_AP_PER_SCAN = 10;
-        private static final int DEFAULT_REPORT_THRESHOLD = 10;
+        private static final int MAX_CHANNELS = 32;
         private static final int DEFAULT_BASE_PERIOD_MS = 5000;
+        private static final int DEFAULT_REPORT_THRESHOLD_NUM_SCANS = 10;
+        private static final int DEFAULT_REPORT_THRESHOLD_PERCENT = 100;
 
         private WifiNative.ScanSettings mSettings;
         {
             mSettings = new WifiNative.ScanSettings();
-            mSettings.max_ap_per_scan = DEFAULT_MAX_AP_PER_SCAN;
+            mSettings.max_ap_per_scan = mScanCapabilities.max_ap_cache_per_scan;
             mSettings.base_period_ms = DEFAULT_BASE_PERIOD_MS;
-            mSettings.report_threshold = DEFAULT_REPORT_THRESHOLD;
+            mSettings.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENT;
+            mSettings.report_threshold_num_scans = DEFAULT_REPORT_THRESHOLD_NUM_SCANS;
 
-            mSettings.buckets = new WifiNative.BucketSettings[MAX_BUCKETS];
+            mSettings.buckets = new WifiNative.BucketSettings[mScanCapabilities.max_scan_buckets];
             for (int i = 0; i < mSettings.buckets.length; i++) {
                 WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
                 bucketSettings.bucket = i;
                 bucketSettings.report_events = 0;
                 bucketSettings.channels = new WifiNative.ChannelSettings[MAX_CHANNELS];
                 bucketSettings.num_channels = 0;
-                for (int j = 0; j < bucketSettings.channels.length; j++) {
-                    WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
-                    bucketSettings.channels[j] = channelSettings;
-                }
                 mSettings.buckets[i] = bucketSettings;
             }
         }
 
         HashMap<Integer, Integer> mChannelToBucketMap = new HashMap<Integer, Integer>();
 
-        private int getBestBucket(WifiScanner.ScanSettings settings) {
+        private int getBestBucket(ScanSettings settings) {
 
             // check to see if any of the channels are being scanned already
             // and find the smallest bucket index (it represents the quickest
             // period of scan)
 
-            WifiScanner.ChannelSpec channels[] = settings.channels;
+            ChannelSpec channels[] = settings.channels;
             if (channels == null) {
                 // set channels based on band
                 channels = getChannelsForBand(settings.band);
@@ -805,13 +977,13 @@
 
             if (channels == null) {
                 // still no channels; then there's nothing to scan
-                Log.e(TAG, "No channels to scan!!");
+                loge("No channels to scan!!");
                 return -1;
             }
 
             int mostFrequentBucketIndex = mTimeBuckets.length;
 
-            for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+            for (ChannelSpec desiredChannelSpec : channels) {
                 if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) {
                     int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency);
                     if (bucket < mostFrequentBucketIndex) {
@@ -832,36 +1004,36 @@
             }
 
             if (mostFrequentBucketIndex < bestBucketIndex) {
-                for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+                for (ChannelSpec desiredChannelSpec : channels) {
                     mChannelToBucketMap.put(desiredChannelSpec.frequency, mostFrequentBucketIndex);
                 }
-                Log.d(TAG, "returning mf bucket number " + mostFrequentBucketIndex);
+                localLog("returning mf bucket number " + mostFrequentBucketIndex);
                 return mostFrequentBucketIndex;
             } else if (bestBucketIndex != -1) {
-                for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+                for (ChannelSpec desiredChannelSpec : channels) {
                     mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex);
                 }
-                Log.d(TAG, "returning best bucket number " + bestBucketIndex);
+                localLog("returning best bucket number " + bestBucketIndex);
                 return bestBucketIndex;
             }
 
-            Log.e(TAG, "Could not find suitable bucket for period " + settings.periodInMs);
+            loge("Could not find suitable bucket for period " + settings.periodInMs);
             return -1;
         }
 
-        void prepChannelMap(WifiScanner.ScanSettings settings) {
+        void prepChannelMap(ScanSettings settings) {
             getBestBucket(settings);
         }
 
-        int addScanRequestToBucket(WifiScanner.ScanSettings settings) {
+        int addScanRequestToBucket(ScanSettings settings) {
 
             int bucketIndex = getBestBucket(settings);
             if (bucketIndex == -1) {
-                Log.e(TAG, "Ignoring invalid settings");
+                loge("Ignoring invalid settings");
                 return -1;
             }
 
-            WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+            ChannelSpec desiredChannels[] = settings.channels;
             if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
                     || desiredChannels == null
                     || desiredChannels.length == 0) {
@@ -869,28 +1041,29 @@
                 desiredChannels = getChannelsForBand(settings.band);
                 if (desiredChannels == null) {
                     // still no channels; then there's nothing to scan
-                    Log.e(TAG, "No channels to scan!!");
+                    loge("No channels to scan!!");
                     return -1;
                 }
             }
 
             // merge the channel lists for these buckets
-            Log.d(TAG, "merging " + desiredChannels.length + " channels "
-                    + " for period " + settings.periodInMs);
+            localLog("merging " + desiredChannels.length + " channels "
+                    + " for period " + settings.periodInMs
+                    + " maxScans " + settings.maxScansToCache);
 
             WifiNative.BucketSettings bucket = mSettings.buckets[bucketIndex];
             boolean added = (bucket.num_channels == 0)
                     && (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED);
-            Log.d(TAG, "existing " + bucket.num_channels + " channels ");
+            localLog("existing " + bucket.num_channels + " channels ");
 
-            HashSet<WifiScanner.ChannelSpec> newChannels = new HashSet<WifiScanner.ChannelSpec>();
-            for (WifiScanner.ChannelSpec desiredChannelSpec : desiredChannels) {
+            HashSet<ChannelSpec> newChannels = new HashSet<ChannelSpec>();
+            for (ChannelSpec desiredChannelSpec : desiredChannels) {
 
-                Log.d(TAG, "desired channel " + desiredChannelSpec.frequency);
+                if (DBG) localLog("desired channel " + desiredChannelSpec.frequency);
 
                 boolean found = false;
-                for (WifiNative.ChannelSettings existingChannelSpec : bucket.channels) {
-                    if (desiredChannelSpec.frequency == existingChannelSpec.frequency) {
+                for (int i = 0; i < bucket.num_channels; i++) {
+                    if (desiredChannelSpec.frequency == bucket.channels[i].frequency) {
                         found = true;
                         break;
                     }
@@ -899,7 +1072,7 @@
                 if (!found) {
                     newChannels.add(desiredChannelSpec);
                 } else {
-                    if (DBG) Log.d(TAG, "Already scanning channel " + desiredChannelSpec.frequency);
+                    if (DBG) localLog("Already scanning channel " + desiredChannelSpec.frequency);
                 }
             }
 
@@ -910,24 +1083,25 @@
                 bucket.band = getBandFromChannels(bucket.channels)
                         | getBandFromChannels(desiredChannels);
                 bucket.channels = new WifiNative.ChannelSettings[0];
-                Log.d(TAG, "switching to using band " + bucket.band);
+                localLog("switching to using band " + bucket.band);
             } else {
-                for (WifiScanner.ChannelSpec desiredChannelSpec : newChannels) {
+                for (ChannelSpec desiredChannelSpec : newChannels) {
 
-                    Log.d(TAG, "adding new channel spec " + desiredChannelSpec.frequency);
+                    localLog("adding new channel spec " + desiredChannelSpec.frequency);
 
-                    WifiNative.ChannelSettings channelSettings = bucket.channels[bucket.num_channels];
+                    WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
                     channelSettings.frequency = desiredChannelSpec.frequency;
+                    bucket.channels[bucket.num_channels] = channelSettings;
                     bucket.num_channels++;
                     mChannelToBucketMap.put(bucketIndex, channelSettings.frequency);
                 }
             }
 
             if (bucket.report_events < settings.reportEvents) {
-                if (DBG) Log.d(TAG, "setting report_events to " + settings.reportEvents);
+                if (DBG) localLog("setting report_events to " + settings.reportEvents);
                 bucket.report_events = settings.reportEvents;
             } else {
-                if (DBG) Log.d(TAG, "report_events is " + settings.reportEvents);
+                if (DBG) localLog("report_events is " + settings.reportEvents);
             }
 
             if (added) {
@@ -935,8 +1109,16 @@
                 mSettings.num_buckets++;
             }
 
-            if (mSettings.max_ap_per_scan < settings.numBssidsPerScan) {
-                mSettings.max_ap_per_scan = settings.numBssidsPerScan;
+            if ( settings.numBssidsPerScan != 0) {
+                if (mSettings.max_ap_per_scan > settings.numBssidsPerScan) {
+                    mSettings.max_ap_per_scan = settings.numBssidsPerScan;
+                }
+            }
+
+            if (settings.maxScansToCache != 0) {
+                if (mSettings.report_threshold_num_scans > settings.maxScansToCache) {
+                    mSettings.report_threshold_num_scans = settings.maxScansToCache;
+                }
             }
 
             return bucket.period_ms;
@@ -980,13 +1162,13 @@
         for (ClientInfo ci : clients) {
             Iterator it = ci.getScans();
             while (it.hasNext()) {
-                Map.Entry<Integer, WifiScanner.ScanSettings> entry =
-                        (Map.Entry<Integer,WifiScanner.ScanSettings>)it.next();
+                Map.Entry<Integer, ScanSettings> entry =
+                        (Map.Entry<Integer,ScanSettings>)it.next();
                 int id = entry.getKey();
                 ScanSettings s = entry.getValue();
                 int newPeriodInMs = c.addScanRequestToBucket(s);
                 if (newPeriodInMs  == -1) {
-                    if (DBG) Log.d(TAG, "could not find a good bucket");
+                    if (DBG) localLog("could not find a good bucket");
                     return false;
                 }
                 if (newPeriodInMs != s.periodInMs) {
@@ -999,25 +1181,46 @@
 
         WifiNative.ScanSettings s = c.getComputedSettings();
         if (s.num_buckets == 0) {
-            if (DBG) Log.d(TAG, "Stopping scan because there are no buckets");
+            if (DBG) localLog("Stopping scan because there are no buckets");
             WifiNative.stopScan();
             return true;
         } else {
             if (WifiNative.startScan(s, mStateMachine)) {
-                if (DBG) Log.d(TAG, "Successfully started scan of " + s.num_buckets + " buckets at"
-                        + "time = " + SystemClock.elapsedRealtimeNanos()/1000);
+                localLog("Successfully started scan of " + s.num_buckets + " buckets at"
+                        + "time = " + SystemClock.elapsedRealtimeNanos() / 1000 + " period "
+                        + s.base_period_ms);
                 return true;
             } else {
-                if (DBG) Log.d(TAG, "Failed to start scan of " + s.num_buckets + " buckets");
+                loge("Failed to start scan of " + s.num_buckets + " buckets");
                 return false;
             }
         }
     }
 
+    void logScanRequest(String request, ClientInfo ci, int id, ScanSettings settings) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(request);
+        sb.append("\nClient ");
+        sb.append(ci.toString());
+        sb.append("\nId ");
+        sb.append(id);
+        sb.append("\n");
+        if (settings != null) {
+            sb.append(describe(settings));
+            sb.append("\n");
+        }
+        sb.append("\n");
+        localLog(sb.toString());
+    }
+
     boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
         // sanity check the input
+        if (ci == null) {
+            Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
+            return false;
+        }
         if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) {
-            Log.d(TAG, "Failing scan request because periodInMs is " + settings.periodInMs);
+            localLog("Failing scan request because periodInMs is " + settings.periodInMs);
             return false;
         }
 
@@ -1040,30 +1243,63 @@
         }
 
         if (settings.periodInMs < minSupportedPeriodMs) {
-            Log.d(TAG, "Failing scan request because minSupportedPeriodMs is "
+            localLog("Failing scan request because minSupportedPeriodMs is "
                     + minSupportedPeriodMs + " but the request wants " + settings.periodInMs);
             return false;
         }
 
+        logScanRequest("addScanRequest", ci, handler, settings);
         ci.addScanRequest(settings, handler);
         if (resetBuckets()) {
             return true;
         } else {
             ci.removeScanRequest(handler);
-            Log.d(TAG, "Failing scan request because failed to reset scan");
+            localLog("Failing scan request because failed to reset scan");
+            return false;
+        }
+    }
+
+    boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
+        if (ci == null) {
+            Log.d(TAG, "Failing single scan request ClientInfo not found " + handler);
+            return false;
+        }
+        if (settings.reportEvents == 0) {
+            settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
+        }
+        if (settings.periodInMs == 0) {
+            settings.periodInMs = 10000;        // 10s - although second scan should never happen
+        }
+
+        logScanRequest("addSingleScanRequest", ci, handler, settings);
+        ci.addScanRequest(settings, handler);
+        if (resetBuckets()) {
+            /* reset periodInMs to 0 to indicate single shot scan */
+            settings.periodInMs = 0;
+            return true;
+        } else {
+            ci.removeScanRequest(handler);
+            localLog("Failing scan request because failed to reset scan");
             return false;
         }
     }
 
     void removeScanRequest(ClientInfo ci, int handler) {
-        ci.removeScanRequest(handler);
-        resetBuckets();
+        if (ci != null) {
+            logScanRequest("removeScanRequest", ci, handler, null);
+            ci.removeScanRequest(handler);
+            resetBuckets();
+        }
     }
 
-    ScanResult[] getScanResults(ClientInfo ci) {
-        ScanResult results[] = WifiNative.getScanResults();
-        ci.reportScanResults(results);
-        return results;
+    boolean reportScanResults() {
+        ScanData results[] = WifiNative.getScanResults(/* flush = */ true);
+        Collection<ClientInfo> clients = mClients.values();
+        for (ClientInfo ci2 : clients) {
+            ci2.reportScanResults(results);
+        }
+
+        return true;
     }
 
     void resetHotlist() {
@@ -1080,7 +1316,7 @@
         if (num_hotlist_ap == 0) {
             WifiNative.resetHotlist();
         } else {
-            WifiScanner.BssidInfo bssidInfos[] = new WifiScanner.BssidInfo[num_hotlist_ap];
+            BssidInfo bssidInfos[] = new BssidInfo[num_hotlist_ap];
             int index = 0;
             for (ClientInfo ci : clients) {
                 Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings();
@@ -1220,12 +1456,12 @@
         class DefaultState extends State {
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "Entering IdleState");
+                if (DBG) localLog("Entering IdleState");
             }
 
             @Override
             public boolean processMessage(Message msg) {
-                if (DBG) Log.d(TAG, "DefaultState state got " + msg);
+                if (DBG) localLog("DefaultState state got " + msg);
                 switch (msg.what) {
                     case WIFI_CHANGE_CMD_ENABLE :
                         transitionTo(mMovingState);
@@ -1250,24 +1486,24 @@
         class StationaryState extends State {
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "Entering StationaryState");
+                if (DBG) localLog("Entering StationaryState");
                 reportWifiStabilized(mCurrentBssids);
             }
 
             @Override
             public boolean processMessage(Message msg) {
-                if (DBG) Log.d(TAG, "Stationary state got " + msg);
+                if (DBG) localLog("Stationary state got " + msg);
                 switch (msg.what) {
                     case WIFI_CHANGE_CMD_ENABLE :
                         // do nothing
                         break;
                     case WIFI_CHANGE_CMD_CHANGE_DETECTED:
-                        if (DBG) Log.d(TAG, "Got wifi change detected");
+                        if (DBG) localLog("Got wifi change detected");
                         reportWifiChanged((ScanResult[])msg.obj);
                         transitionTo(mMovingState);
                         break;
                     case WIFI_CHANGE_CMD_DISABLE:
-                        if (DBG) Log.d(TAG, "Got Disable Wifi Change");
+                        if (DBG) localLog("Got Disable Wifi Change");
                         mCurrentBssids = null;
                         removeScanRequest();
                         untrackSignificantWifiChange();
@@ -1290,29 +1526,30 @@
 
             @Override
             public void enter() {
-                if (DBG) Log.d(TAG, "Entering MovingState");
+                if (DBG) localLog("Entering MovingState");
                 issueFullScan();
             }
 
             @Override
             public boolean processMessage(Message msg) {
-                if (DBG) Log.d(TAG, "MovingState state got " + msg);
+                if (DBG) localLog("MovingState state got " + msg);
                 switch (msg.what) {
                     case WIFI_CHANGE_CMD_ENABLE :
                         // do nothing
                         break;
                     case WIFI_CHANGE_CMD_DISABLE:
-                        if (DBG) Log.d(TAG, "Got Disable Wifi Change");
+                        if (DBG) localLog("Got Disable Wifi Change");
                         mCurrentBssids = null;
                         removeScanRequest();
                         untrackSignificantWifiChange();
                         transitionTo(mDefaultState);
                         break;
                     case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
-                        if (DBG) Log.d(TAG, "Got scan results");
+                        if (DBG) localLog("Got scan results");
                         if (mScanResultsPending) {
-                            if (DBG) Log.d(TAG, "reconfiguring scan");
-                            reconfigureScan((ScanResult[])msg.obj, STATIONARY_SCAN_PERIOD_MS);
+                            if (DBG) localLog("reconfiguring scan");
+                            reconfigureScan((ScanData[])msg.obj,
+                                    STATIONARY_SCAN_PERIOD_MS);
                             mWifiChangeDetected = false;
                             mAlarmManager.setExact(AlarmManager.RTC_WAKEUP,
                                     System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS,
@@ -1321,7 +1558,7 @@
                         }
                         break;
                     case WIFI_CHANGE_CMD_CONFIGURE:
-                        if (DBG) Log.d(TAG, "Got configuration from app");
+                        if (DBG) localLog("Got configuration from app");
                         WifiScanner.WifiChangeSettings settings =
                                 (WifiScanner.WifiChangeSettings) msg.obj;
                         reconfigureScan(settings);
@@ -1333,14 +1570,14 @@
                                 mTimeoutIntent);
                         break;
                     case WIFI_CHANGE_CMD_CHANGE_DETECTED:
-                        if (DBG) Log.d(TAG, "Change detected");
+                        if (DBG) localLog("Change detected");
                         mAlarmManager.cancel(mTimeoutIntent);
                         reportWifiChanged((ScanResult[])msg.obj);
                         mWifiChangeDetected = true;
                         issueFullScan();
                         break;
                     case WIFI_CHANGE_CMD_CHANGE_TIMEOUT:
-                        if (DBG) Log.d(TAG, "Got timeout event");
+                        if (DBG) localLog("Got timeout event");
                         if (mWifiChangeDetected == false) {
                             transitionTo(mStationaryState);
                         }
@@ -1357,8 +1594,8 @@
             }
 
             void issueFullScan() {
-                if (DBG) Log.d(TAG, "Issuing full scan");
-                WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+                if (DBG) localLog("Issuing full scan");
+                ScanSettings settings = new ScanSettings();
                 settings.band = WifiScanner.WIFI_BAND_BOTH;
                 settings.periodInMs = MOVING_SCAN_PERIOD_MS;
                 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
@@ -1368,10 +1605,10 @@
 
         }
 
-        void reconfigureScan(ScanResult[] results, int period) {
+        void reconfigureScan(ScanData[] results, int period) {
             // find brightest APs and set them as sentinels
             if (results.length < MAX_APS_TO_TRACK) {
-                Log.d(TAG, "too few APs (" + results.length + ") available to track wifi change");
+                localLog("too few APs (" + results.length + ") available to track wifi change");
                 return;
             }
 
@@ -1379,7 +1616,7 @@
 
             // remove duplicate BSSIDs
             HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
-            for (ScanResult result : results) {
+            for (ScanResult result : results[0].getResults()) {
                 ScanResult saved = bssidToScanResult.get(result.BSSID);
                 if (saved == null) {
                     bssidToScanResult.put(result.BSSID, result);
@@ -1418,14 +1655,14 @@
                 }
             }
 
-            if (DBG) Log.d(TAG, "Found " + channels.size() + " channels");
+            if (DBG) localLog("Found " + channels.size() + " channels");
 
             // set scanning schedule
-            WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+            ScanSettings settings = new ScanSettings();
             settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
-            settings.channels = new WifiScanner.ChannelSpec[channels.size()];
+            settings.channels = new ChannelSpec[channels.size()];
             for (int i = 0; i < channels.size(); i++) {
-                settings.channels[i] = new WifiScanner.ChannelSpec(channels.get(i));
+                settings.channels[i] = new ChannelSpec(channels.get(i));
             }
 
             settings.periodInMs = period;
@@ -1436,17 +1673,17 @@
             settings2.lostApSampleSize = 3;
             settings2.unchangedSampleSize = 3;
             settings2.minApsBreachingThreshold = 2;
-            settings2.bssidInfos = new WifiScanner.BssidInfo[brightest.length];
+            settings2.bssidInfos = new BssidInfo[brightest.length];
 
             for (int i = 0; i < brightest.length; i++) {
-                WifiScanner.BssidInfo BssidInfo = new WifiScanner.BssidInfo();
+                BssidInfo BssidInfo = new BssidInfo();
                 BssidInfo.bssid = brightest[i].BSSID;
                 int threshold = (100 + brightest[i].level) / 32 + 2;
                 BssidInfo.low = brightest[i].level - threshold;
                 BssidInfo.high = brightest[i].level + threshold;
                 settings2.bssidInfos[i] = BssidInfo;
 
-                if (DBG) Log.d(TAG, "Setting bssid=" + BssidInfo.bssid + ", " +
+                if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " +
                         "low=" + BssidInfo.low + ", high=" + BssidInfo.high);
             }
 
@@ -1457,12 +1694,12 @@
         void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
 
             if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
-                Log.d(TAG, "too few APs (" + settings.bssidInfos.length
+                localLog("too few APs (" + settings.bssidInfos.length
                         + ") available to track wifi change");
                 return;
             }
 
-            if (DBG) Log.d(TAG, "Setting configuration specified by app");
+            if (DBG) localLog("Setting configuration specified by app");
 
             mCurrentBssids = new ScanResult[settings.bssidInfos.length];
             HashSet<Integer> channels = new HashSet<Integer>();
@@ -1478,12 +1715,12 @@
             removeScanRequest();
 
             // set new scanning schedule
-            WifiScanner.ScanSettings settings2 = new WifiScanner.ScanSettings();
+            ScanSettings settings2 = new ScanSettings();
             settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
-            settings2.channels = new WifiScanner.ChannelSpec[channels.size()];
+            settings2.channels = new ChannelSpec[channels.size()];
             int i = 0;
             for (Integer channel : channels) {
-                settings2.channels[i++] = new WifiScanner.ChannelSpec(channel);
+                settings2.channels[i++] = new ChannelSpec(channel);
             }
 
             settings2.periodInMs = settings.periodInMs;
@@ -1495,11 +1732,11 @@
 
         class ClientInfoLocal extends ClientInfo {
             ClientInfoLocal() {
-                super(null, null);
+                super(0, null, null);
             }
             @Override
-            void deliverScanResults(int handler, ScanResult results[]) {
-                if (DBG) Log.d(TAG, "Delivering messages directly");
+            void deliverScanResults(int handler, ScanData results[]) {
+                if (DBG) localLog("Delivering messages directly");
                 sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results);
             }
             @Override
@@ -1516,8 +1753,8 @@
         ClientInfo mClientInfo = new ClientInfoLocal();
         private static final int SCAN_COMMAND_ID = 1;
 
-        void addScanRequest(WifiScanner.ScanSettings settings) {
-            if (DBG) Log.d(TAG, "Starting scans");
+        void addScanRequest(ScanSettings settings) {
+            if (DBG) localLog("Starting scans");
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN;
             msg.arg2 = SCAN_COMMAND_ID;
@@ -1526,7 +1763,7 @@
         }
 
         void removeScanRequest() {
-            if (DBG) Log.d(TAG, "Stopping scans");
+            if (DBG) localLog("Stopping scans");
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN;
             msg.arg2 = SCAN_COMMAND_ID;
@@ -1544,44 +1781,156 @@
 
     }
 
-    private static WifiScanner.ChannelSpec[] getChannelsForBand(int band) {
-        int channels[] = WifiNative.getChannelsForBand(band);
-        if (channels != null) {
-            WifiScanner.ChannelSpec channelSpecs[] = new WifiScanner.ChannelSpec[channels.length];
-            for (int i = 0; i < channels.length; i++) {
-                channelSpecs[i] = new WifiScanner.ChannelSpec(channels[i]);
-            }
-            return channelSpecs;
-        } else {
-            return new WifiScanner.ChannelSpec[0];
+    private static ChannelSpec mChannels[][];
+
+    private static void copyChannels(
+            ChannelSpec channelSpec[], int offset, int channels[]) {
+        for (int i = 0; i < channels.length; i++) {
+            channelSpec[offset +i] = new ChannelSpec(channels[i]);
         }
     }
 
-    private static int getBandFromChannels(WifiScanner.ChannelSpec[] channels) {
+    private static boolean initChannels() {
+        if (mChannels != null) {
+            /* already initialized */
+            return true;
+        }
+
+        int channels24[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
+        if (channels24 == null) {
+            loge("Could not get channels for 2.4 GHz");
+            return false;
+        }
+
+        int channels5[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
+        if (channels5 == null) {
+            loge("Could not get channels for 5 GHz");
+            return false;
+        }
+
+        int channelsDfs[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
+        if (channelsDfs == null) {
+            loge("Could not get channels for DFS");
+            return false;
+        }
+
+        mChannels = new ChannelSpec[8][];
+
+        mChannels[0] = new ChannelSpec[0];
+
+        mChannels[1] = new ChannelSpec[channels24.length];
+        copyChannels(mChannels[1], 0, channels24);
+
+        mChannels[2] = new ChannelSpec[channels5.length];
+        copyChannels(mChannels[2], 0, channels5);
+
+        mChannels[3] = new ChannelSpec[channels24.length + channels5.length];
+        copyChannels(mChannels[3], 0, channels24);
+        copyChannels(mChannels[3], channels24.length, channels5);
+
+        mChannels[4] = new ChannelSpec[channelsDfs.length];
+        copyChannels(mChannels[4], 0, channelsDfs);
+
+        mChannels[5] = new ChannelSpec[channels24.length + channelsDfs.length];
+        copyChannels(mChannels[5], 0, channels24);
+        copyChannels(mChannels[5], channels24.length, channelsDfs);
+
+        mChannels[6] = new ChannelSpec[channels5.length + channelsDfs.length];
+        copyChannels(mChannels[6], 0, channels5);
+        copyChannels(mChannels[6], channels5.length, channelsDfs);
+
+        mChannels[7] = new ChannelSpec[
+                channels24.length + channels5.length + channelsDfs.length];
+        copyChannels(mChannels[7], 0, channels24);
+        copyChannels(mChannels[7], channels24.length, channels5);
+        copyChannels(mChannels[7], channels24.length + channels5.length, channelsDfs);
+
+        return true;
+    }
+
+    private static ChannelSpec[] getChannelsForBand(int band) {
+        initChannels();
+
+        if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS)
+            /* invalid value for band */
+            return mChannels[0];
+        else
+            return mChannels[band];
+    }
+
+    private static boolean isDfs(int channel) {
+        ChannelSpec[] dfsChannels = getChannelsForBand(WifiScanner
+                .WIFI_BAND_5_GHZ_DFS_ONLY);
+        for (int i = 0; i < dfsChannels.length; i++) {
+            if (channel == dfsChannels[i].frequency) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static int getBandFromChannels(ChannelSpec[] channels) {
         int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
-        for (WifiScanner.ChannelSpec channel : channels) {
+        for (ChannelSpec channel : channels) {
             if (2400 <= channel.frequency && channel.frequency < 2500) {
                 band |= WifiScanner.WIFI_BAND_24_GHZ;
+            } else if ( isDfs(channel.frequency)) {
+                band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
             } else if (5100 <= channel.frequency && channel.frequency < 6000) {
                 band |= WifiScanner.WIFI_BAND_5_GHZ;
-            } else {
-                /* TODO: Add DFS Range */
             }
         }
         return band;
     }
+
     private static int getBandFromChannels(WifiNative.ChannelSettings[] channels) {
         int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
         for (WifiNative.ChannelSettings channel : channels) {
-            if (2400 <= channel.frequency && channel.frequency < 2500) {
-                band |= WifiScanner.WIFI_BAND_24_GHZ;
-            } else if (5100 <= channel.frequency && channel.frequency < 6000) {
-                band |= WifiScanner.WIFI_BAND_5_GHZ;
-            } else {
-                /* TODO: Add DFS Range */
+            if (channel != null) {
+                if (2400 <= channel.frequency && channel.frequency < 2500) {
+                    band |= WifiScanner.WIFI_BAND_24_GHZ;
+                } else if ( isDfs(channel.frequency)) {
+                    band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
+                } else if (5100 <= channel.frequency && channel.frequency < 6000) {
+                    band |= WifiScanner.WIFI_BAND_5_GHZ;
+                }
             }
         }
         return band;
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump WifiScanner from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " without permission "
+                    + android.Manifest.permission.DUMP);
+            return;
+        }
+        mStateMachine.dump(fd, pw, args);
+    }
+
+    static String describe(ScanSettings scanSettings) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("  band:").append(scanSettings.band);
+        sb.append("  period:").append(scanSettings.periodInMs);
+        sb.append("  reportEvents:").append(scanSettings.reportEvents);
+        sb.append("  numBssidsPerScan:").append(scanSettings.numBssidsPerScan);
+        sb.append("  maxScansToCache:").append(scanSettings.maxScansToCache).append("\n");
+
+        sb.append("  channels: ");
+
+        if (scanSettings.channels != null) {
+            for (int i = 0; i < scanSettings.channels.length; i++) {
+                sb.append(scanSettings.channels[i].frequency);
+                sb.append(" ");
+            }
+        }
+        sb.append("\n");
+        return sb.toString();
+    }
+
 }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index cc5824e..46058ad 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -30,45 +30,71 @@
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpResults;
-import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
+import android.net.Network;
+import android.net.NetworkScorerAppManager;
 import android.net.NetworkUtils;
-import android.net.RouteInfo;
-import android.net.wifi.*;
+import android.net.Uri;
+import android.net.wifi.BatchedScanResult;
+import android.net.wifi.BatchedScanSettings;
 import android.net.wifi.IWifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.ScanSettings;
+import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiChannel;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConnectionStatistics;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
-import java.io.FileNotFoundException;
-import java.io.BufferedReader;
-import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.Override;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.util.ArrayList;
-import java.util.List;
-
 import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.AsyncChannel;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.wifi.configparse.ConfigBuilder;
+
+import org.xml.sax.SAXException;
+
+import java.io.BufferedReader;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
 import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
@@ -89,6 +115,7 @@
 public final class WifiServiceImpl extends IWifiManager.Stub {
     private static final String TAG = "WifiService";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     final WifiStateMachine mWifiStateMachine;
 
@@ -109,7 +136,9 @@
     private int mMulticastDisabled;
 
     private final IBatteryStats mBatteryStats;
+    private final PowerManager mPowerManager;
     private final AppOpsManager mAppOps;
+    private final UserManager mUserManager;
 
     private String mInterfaceName;
 
@@ -123,8 +152,6 @@
     /* Tracks the persisted states for wi-fi & airplane mode */
     final WifiSettingsStore mSettingsStore;
 
-    final boolean mBatchedScanSupported;
-
     /**
      * Asynchronous channel to WifiStateMachine
      */
@@ -173,31 +200,21 @@
                     WifiConfiguration config = (WifiConfiguration) msg.obj;
                     int networkId = msg.arg1;
                     if (msg.what == WifiManager.SAVE_NETWORK) {
-                        if (config != null) {
-                            if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
-                                config.creatorUid = Binder.getCallingUid();
-                            } else {
-                                config.lastUpdateUid = Binder.getCallingUid();
-                            }
-                        }
                         Slog.e("WiFiServiceImpl ", "SAVE"
                                 + " nid=" + Integer.toString(networkId)
-                                + " uid=" + Integer.toString(config.creatorUid)
-                                + "/" + Integer.toString(config.lastUpdateUid));
+                                + " uid=" + msg.sendingUid
+                                + " name="
+                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                     }
                     if (msg.what == WifiManager.CONNECT_NETWORK) {
-                        if (config != null) {
-                            if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
-                                config.creatorUid = Binder.getCallingUid();
-                            } else {
-                                config.lastUpdateUid = Binder.getCallingUid();
-                            }
-                        }
                         Slog.e("WiFiServiceImpl ", "CONNECT "
                                 + " nid=" + Integer.toString(networkId)
-                                + " uid=" + Binder.getCallingUid());
+                                + " uid=" + msg.sendingUid
+                                + " name="
+                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                     }
-                    if (config != null && config.isValid()) {
+
+                    if (config != null && isValid(config)) {
                         if (DBG) Slog.d(TAG, "Connect with config" + config);
                         mWifiStateMachine.sendMessage(Message.obtain(msg));
                     } else if (config == null
@@ -306,7 +323,9 @@
         mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);
         mWifiStateMachine.enableRssiPolling(true);
         mBatteryStats = BatteryStatsService.getService();
+        mPowerManager = context.getSystemService(PowerManager.class);
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+        mUserManager = UserManager.get(mContext);
 
         mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
         mSettingsStore = new WifiSettingsStore(mContext);
@@ -316,9 +335,6 @@
         mClientHandler = new ClientHandler(wifiThread.getLooper());
         mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
         mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
-
-        mBatchedScanSupported = mContext.getResources().getBoolean(
-                R.bool.config_wifi_batched_scan_supported);
     }
 
 
@@ -350,6 +366,8 @@
         // can result in race conditions when apps toggle wifi in the background
         // without active user involvement. Always receive broadcasts.
         registerForBroadcasts();
+        registerForPackageOrUserRemoval();
+        mInIdleMode = mPowerManager.isDeviceIdleMode();
 
         mWifiController.start();
 
@@ -390,6 +408,7 @@
 
     // Start a location scan.
     // L release: A location scan is implemented as a normal scan and avoids scanning DFS channels
+    // Deprecated: Will soon remove implementation
     public void startLocationRestrictedScan(WorkSource workSource) {
         enforceChangePermission();
         enforceLocationHardwarePermission();
@@ -421,6 +440,23 @@
      */
     public void startScan(ScanSettings settings, WorkSource workSource) {
         enforceChangePermission();
+        synchronized (this) {
+            if (mInIdleMode) {
+                // Need to send an immediate scan result broadcast in case the
+                // caller is waiting for a result ..
+
+                // clear calling identity to send broadcast
+                long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mWifiStateMachine.sendScanResultsAvailableBroadcast(/* scanSucceeded = */ false);
+                } finally {
+                    // restore calling identity
+                    Binder.restoreCallingIdentity(callingIdentity);
+                }
+                mScanPending = true;
+                return;
+            }
+        }
         if (settings != null) {
             settings = new ScanSettings(settings);
             if (!settings.isValid()) {
@@ -438,42 +474,11 @@
                 settings, workSource);
     }
 
-    private class BatchedScanRequest extends DeathRecipient {
-        final BatchedScanSettings settings;
-        final int uid;
-        final int pid;
-        final WorkSource workSource;
-
-        BatchedScanRequest(BatchedScanSettings settings, IBinder binder, WorkSource ws) {
-            super(0, null, binder, null);
-            this.settings = settings;
-            this.uid = getCallingUid();
-            this.pid = getCallingPid();
-            workSource = ws;
-        }
-        public void binderDied() {
-            stopBatchedScan(settings, uid, pid);
-        }
-        public String toString() {
-            return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}";
-        }
-
-        public boolean isSameApp(int uid, int pid) {
-            return (this.uid == uid && this.pid == pid);
-        }
-    }
-
-    private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>();
-
     public boolean isBatchedScanSupported() {
-        return mBatchedScanSupported;
+        return false;
     }
 
-    public void pollBatchedScan() {
-        enforceChangePermission();
-        if (mBatchedScanSupported == false) return;
-        mWifiStateMachine.requestBatchedScanPoll();
-    }
+    public void pollBatchedScan() { }
 
     public String getWpsNfcConfigurationToken(int netId) {
         enforceConnectivityInternalPermission();
@@ -485,145 +490,36 @@
      */
     public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder,
             WorkSource workSource) {
-        enforceChangePermission();
-        if (workSource != null) {
-            enforceWorkSourcePermission();
-            // WifiManager currently doesn't use names, so need to clear names out of the
-            // supplied WorkSource to allow future WorkSource combining.
-            workSource.clearNames();
-        }
-        if (mBatchedScanSupported == false) return false;
-        requested = new BatchedScanSettings(requested);
-        if (requested.isInvalid()) return false;
-        BatchedScanRequest r = new BatchedScanRequest(requested, binder, workSource);
-        synchronized(mBatchedScanners) {
-            mBatchedScanners.add(r);
-            resolveBatchedScannersLocked();
-        }
-        return true;
+        return false;
     }
 
     public List<BatchedScanResult> getBatchedScanResults(String callingPackage) {
-        enforceAccessPermission();
-        if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>();
-        int uid = Binder.getCallingUid();
-        int userId = UserHandle.getCallingUserId();
-        boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
-                    != AppOpsManager.MODE_ALLOWED) {
-                return new ArrayList<BatchedScanResult>();
-            }
-            if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
-                return new ArrayList<BatchedScanResult>();
-            }
-            return mWifiStateMachine.syncGetBatchedScanResultsList();
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        return null;
     }
 
-    public void stopBatchedScan(BatchedScanSettings settings) {
-        enforceChangePermission();
-        if (mBatchedScanSupported == false) return;
-        stopBatchedScan(settings, getCallingUid(), getCallingPid());
-    }
+    public void stopBatchedScan(BatchedScanSettings settings) { }
 
-    private void stopBatchedScan(BatchedScanSettings settings, int uid, int pid) {
-        ArrayList<BatchedScanRequest> found = new ArrayList<BatchedScanRequest>();
-        synchronized(mBatchedScanners) {
-            for (BatchedScanRequest r : mBatchedScanners) {
-                if (r.isSameApp(uid, pid) && (settings == null || settings.equals(r.settings))) {
-                    found.add(r);
-                    if (settings != null) break;
-                }
-            }
-            for (BatchedScanRequest r : found) {
-                mBatchedScanners.remove(r);
-            }
-            if (found.size() != 0) {
-                resolveBatchedScannersLocked();
-            }
-        }
-    }
+    boolean mInIdleMode;
+    boolean mScanPending;
 
-    private void resolveBatchedScannersLocked() {
-        BatchedScanSettings setting = new BatchedScanSettings();
-        WorkSource responsibleWorkSource = null;
-        int responsibleUid = 0;
-        double responsibleCsph = 0; // Channel Scans Per Hour
-
-        if (mBatchedScanners.size() == 0) {
-            mWifiStateMachine.setBatchedScanSettings(null, 0, 0, null);
-            return;
-        }
-        for (BatchedScanRequest r : mBatchedScanners) {
-            BatchedScanSettings s = r.settings;
-
-            // evaluate responsibility
-            int currentChannelCount;
-            int currentScanInterval;
-            double currentCsph;
-
-            if (s.channelSet == null || s.channelSet.isEmpty()) {
-                // all channels - 11 B and 9 A channels roughly.
-                currentChannelCount = 9 + 11;
-            } else {
-                currentChannelCount = s.channelSet.size();
-                // these are rough est - no real need to correct for reg-domain;
-                if (s.channelSet.contains("A")) currentChannelCount += (9 - 1);
-                if (s.channelSet.contains("B")) currentChannelCount += (11 - 1);
-
-            }
-            if (s.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
-                currentScanInterval = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
-            } else {
-                currentScanInterval = s.scanIntervalSec;
-            }
-            currentCsph = 60 * 60 * currentChannelCount / currentScanInterval;
-
-            if (currentCsph > responsibleCsph) {
-                responsibleUid = r.uid;
-                responsibleWorkSource = r.workSource;
-                responsibleCsph = currentCsph;
-            }
-
-            if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
-                    s.maxScansPerBatch < setting.maxScansPerBatch) {
-                setting.maxScansPerBatch = s.maxScansPerBatch;
-            }
-            if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
-                    (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED ||
-                    s.maxApPerScan > setting.maxApPerScan)) {
-                setting.maxApPerScan = s.maxApPerScan;
-            }
-            if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
-                    s.scanIntervalSec < setting.scanIntervalSec) {
-                setting.scanIntervalSec = s.scanIntervalSec;
-            }
-            if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
-                    (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED ||
-                    s.maxApForDistance > setting.maxApForDistance)) {
-                setting.maxApForDistance = s.maxApForDistance;
-            }
-            if (s.channelSet != null && s.channelSet.size() != 0) {
-                if (setting.channelSet == null || setting.channelSet.size() != 0) {
-                    if (setting.channelSet == null) setting.channelSet = new ArrayList<String>();
-                    for (String i : s.channelSet) {
-                        if (setting.channelSet.contains(i) == false) setting.channelSet.add(i);
+    void handleIdleModeChanged() {
+        boolean doScan = false;
+        synchronized (this) {
+            boolean idle = mPowerManager.isDeviceIdleMode();
+            if (mInIdleMode != idle) {
+                mInIdleMode = idle;
+                if (!idle) {
+                    if (mScanPending) {
+                        mScanPending = false;
+                        doScan = true;
                     }
-                } // else, ignore the constraint - we already use all channels
-            } else {
-                if (setting.channelSet == null || setting.channelSet.size() != 0) {
-                    setting.channelSet = new ArrayList<String>();
                 }
             }
         }
-
-        setting.constrain();
-        mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid, (int)responsibleCsph,
-                responsibleWorkSource);
+        if (doScan) {
+            // Someone requested a scan while we were idle; do a full scan now.
+            startScan(null, null);
+        }
     }
 
     private void enforceAccessPermission() {
@@ -633,7 +529,7 @@
 
     private void enforceChangePermission() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
-                                                "WifiService");
+                "WifiService");
     }
 
     private void enforceLocationHardwarePermission() {
@@ -719,12 +615,11 @@
     public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
         enforceChangePermission();
         ConnectivityManager.enforceTetherChangePermission(mContext);
-        UserManager um = UserManager.get(mContext);
-        if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
         }
         // null wifiConfig is a meaningful input for CMD_SET_AP
-        if (wifiConfig == null || wifiConfig.isValid()) {
+        if (wifiConfig == null || isValid(wifiConfig)) {
             mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
         } else {
             Slog.e(TAG, "Invalid WifiConfiguration");
@@ -754,6 +649,25 @@
     }
 
     /**
+     * see {@link WifiManager#buildWifiConfig()}
+     * @return a WifiConfiguration.
+     */
+    public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
+        if (mimeType.equals(ConfigBuilder.WifiConfigType)) {
+            try {
+                return ConfigBuilder.buildConfig(uriString, data, mContext);
+            }
+            catch (IOException | GeneralSecurityException | SAXException e) {
+                Log.e(TAG, "Failed to parse wi-fi configuration: " + e);
+            }
+        }
+        else {
+            Log.i(TAG, "Unknown wi-fi config type: " + mimeType);
+        }
+        return null;
+    }
+
+    /**
      * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
      * @param wifiConfig WifiConfiguration details for soft access point
      */
@@ -761,7 +675,7 @@
         enforceChangePermission();
         if (wifiConfig == null)
             return;
-        if (wifiConfig.isValid()) {
+        if (isValid(wifiConfig)) {
             mWifiStateMachine.setWifiApConfiguration(wifiConfig);
         } else {
             Slog.e(TAG, "Invalid WifiConfiguration");
@@ -816,7 +730,7 @@
     }
 
     /**
-     * see {@link android.net.wifi.WifiAdapter#reportActivityInfo}
+     * see {@link android.net.wifi.WifiManager#getControllerActivityEnergyInfo(int)}
      */
     public WifiActivityEnergyInfo reportActivityInfo() {
         enforceAccessPermission();
@@ -825,11 +739,39 @@
         if (mWifiStateMachineChannel != null) {
             stats = mWifiStateMachine.syncGetLinkLayerStats(mWifiStateMachineChannel);
             if (stats != null) {
+                final long rxIdleCurrent = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_wifi_idle_receive_cur_ma);
+                final long rxCurrent = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_wifi_active_rx_cur_ma);
+                final long txCurrent = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_wifi_tx_cur_ma);
+                final double voltage = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_wifi_operating_voltage_mv)
+                        / 1000.0;
+
+                final long rxIdleTime = stats.on_time - stats.tx_time - stats.rx_time;
+                final long energyUsed = (long)((stats.tx_time * txCurrent +
+                        stats.rx_time * rxCurrent +
+                        rxIdleTime * rxIdleCurrent) * voltage);
+                if (VDBG || rxIdleTime < 0 || stats.on_time < 0 || stats.tx_time < 0 ||
+                        stats.rx_time < 0 || energyUsed < 0) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(" rxIdleCur=" + rxIdleCurrent);
+                    sb.append(" rxCur=" + rxCurrent);
+                    sb.append(" txCur=" + txCurrent);
+                    sb.append(" voltage=" + voltage);
+                    sb.append(" on_time=" + stats.on_time);
+                    sb.append(" tx_time=" + stats.tx_time);
+                    sb.append(" rx_time=" + stats.rx_time);
+                    sb.append(" rxIdleTime=" + rxIdleTime);
+                    sb.append(" energy=" + energyUsed);
+                    Log.e(TAG, " reportActivityInfo: " + sb.toString());
+                }
+
                 // Convert the LinkLayerStats into EnergyActivity
-                energyInfo = new WifiActivityEnergyInfo(
+                energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(),
                         WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time,
-                        stats.rx_time, stats.on_time - stats.tx_time - stats.rx_time,
-                        0 /* TBD */);
+                        stats.rx_time, rxIdleTime, energyUsed);
             }
             return energyInfo;
         } else {
@@ -869,15 +811,47 @@
     }
 
     /**
+     * Returns a WifiConfiguration matching this ScanResult
+     * @param scanResult scanResult that represents the BSSID
+     * @return {@link WifiConfiguration} that matches this BSSID or null
+     */
+    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
+        enforceAccessPermission();
+        return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
+    }
+
+
+    /**
      * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
      * @return the supplicant-assigned identifier for the new or updated
      * network if the operation succeeds, or {@code -1} if it fails
      */
     public int addOrUpdateNetwork(WifiConfiguration config) {
         enforceChangePermission();
-        if (config.isValid()) {
+        if (isValid(config) && isValidPasspoint(config)) {
+
+            WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+
+            if (config.isPasspoint() &&
+                    (enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
+                enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS)) {
+                try {
+                    verifyCert(enterpriseConfig.getCaCertificate());
+                } catch (CertPathValidatorException cpve) {
+                    Slog.e(TAG, "CA Cert " +
+                            enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
+                            " untrusted: " + cpve.getMessage());
+                    return -1;
+                } catch (GeneralSecurityException | IOException e) {
+                    Slog.e(TAG, "Failed to verify certificate" +
+                            enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
+                            ": " + e);
+                    return -1;
+                }
+            }
+
             //TODO: pass the Uid the WifiStateMachine as a message parameter
-            Slog.e("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+            Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
                     + " SSID " + config.SSID
                     + " nid=" + Integer.toString(config.networkId));
             if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
@@ -897,7 +871,21 @@
         }
     }
 
-     /**
+    public static void verifyCert(X509Certificate caCert)
+            throws GeneralSecurityException, IOException {
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        CertPathValidator validator =
+                CertPathValidator.getInstance(CertPathValidator.getDefaultType());
+        CertPath path = factory.generateCertPath(
+                Arrays.asList(caCert));
+        KeyStore ks = KeyStore.getInstance("AndroidCAStore");
+        ks.load(null, null);
+        PKIXParameters params = new PKIXParameters(ks);
+        params.setRevocationEnabled(false);
+        validator.validate(path, params);
+    }
+
+    /**
      * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
      * @param netId the integer that identifies the network configuration
      * to the supplicant
@@ -975,9 +963,20 @@
         enforceAccessPermission();
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
+        boolean canReadPeerMacAddresses = checkPeersMacAddress();
+        boolean isActiveNetworkScorer =
+                NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
         boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
         long ident = Binder.clearCallingIdentity();
         try {
+            if (!canReadPeerMacAddresses && !isActiveNetworkScorer
+                    && !isLocationEnabled()) {
+                return new ArrayList<ScanResult>();
+            }
+            if (!canReadPeerMacAddresses && !isActiveNetworkScorer
+                    && !checkCallerCanAccessScanResults(callingPackage, uid)) {
+                return new ArrayList<ScanResult>();
+            }
             if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
                     != AppOpsManager.MODE_ALLOWED) {
                 return new ArrayList<ScanResult>();
@@ -991,6 +990,11 @@
         }
     }
 
+    private boolean isLocationEnabled() {
+        return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
+                Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF;
+    }
+
     /**
      * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
      */
@@ -1001,6 +1005,14 @@
     }
 
     /**
+     * Returns true if the caller holds PEERS_MAC_ADDRESS.
+     */
+    private boolean checkPeersMacAddress() {
+        return mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
+    }
+
+    /**
      * Returns true if the calling user is the current one or a profile of the
      * current user..
      */
@@ -1009,7 +1021,7 @@
         if (userId == currentUser) {
             return true;
         }
-        List<UserInfo> profiles = UserManager.get(mContext).getProfiles(currentUser);
+        List<UserInfo> profiles = mUserManager.getProfiles(currentUser);
         for (UserInfo user : profiles) {
             if (userId == user.id) {
                 return true;
@@ -1031,7 +1043,7 @@
             if (userId == ownerUser) {
                 return true;
             }
-            List<UserInfo> profiles = UserManager.get(mContext).getProfiles(ownerUser);
+            List<UserInfo> profiles = mUserManager.getProfiles(ownerUser);
             for (UserInfo profile : profiles) {
                 if (userId == profile.id) {
                     return true;
@@ -1083,6 +1095,15 @@
         }
     }
 
+     /**
+     * Get the country code
+     * @return ISO 3166 country code.
+     */
+    public String getCountryCode() {
+        enforceConnectivityInternalPermission();
+        String country = mWifiStateMachine.getCountryCode();
+        return country;
+    }
     /**
      * Set the operational frequency band
      * @param band One of
@@ -1350,6 +1371,8 @@
             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
                 boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
                 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
+            } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+                handleIdleModeChanged();
             }
         }
     };
@@ -1380,9 +1403,41 @@
         intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+        intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         mContext.registerReceiver(mReceiver, intentFilter);
     }
 
+    private void registerForPackageOrUserRemoval() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                switch (intent.getAction()) {
+                    case Intent.ACTION_PACKAGE_REMOVED: {
+                        if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                            return;
+                        }
+                        int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                        Uri uri = intent.getData();
+                        if (uid == -1 || uri == null) {
+                            return;
+                        }
+                        String pkgName = uri.getSchemeSpecificPart();
+                        mWifiStateMachine.removeAppConfigs(pkgName, uid);
+                        break;
+                    }
+                    case Intent.ACTION_USER_REMOVED: {
+                        int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                        mWifiStateMachine.removeUserConfigs(userHandle);
+                        break;
+                    }
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1398,6 +1453,8 @@
                                        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
         pw.println("mMulticastEnabled " + mMulticastEnabled);
         pw.println("mMulticastDisabled " + mMulticastDisabled);
+        pw.println("mInIdleMode " + mInIdleMode);
+        pw.println("mScanPending " + mScanPending);
         mWifiController.dump(fd, pw, args);
         mSettingsStore.dump(fd, pw, args);
         mNotificationController.dump(fd, pw, args);
@@ -1439,6 +1496,12 @@
         pw.println("Locks held:");
         mLocks.dump(pw);
 
+        pw.println("Multicast Locks held:");
+        for (Multicaster l : mMulticasters) {
+            pw.print("    ");
+            pw.println(l);
+        }
+
         mWifiWatchdogStateMachine.dump(fd, pw, args);
         pw.println();
         mWifiStateMachine.dump(fd, pw, args);
@@ -1840,6 +1903,25 @@
         return mWifiStateMachine.getAllowScansWithTraffic();
     }
 
+    public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+        enforceChangePermission();
+        return mWifiStateMachine.enableAutoJoinWhenAssociated(enabled);
+    }
+
+    public boolean getEnableAutoJoinWhenAssociated() {
+        enforceAccessPermission();
+        return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
+    }
+    public void setHalBasedAutojoinOffload(int enabled) {
+        enforceChangePermission();
+        mWifiStateMachine.setHalBasedAutojoinOffload(enabled);
+    }
+
+    public int getHalBasedAutojoinOffload() {
+        enforceAccessPermission();
+        return mWifiStateMachine.getHalBasedAutojoinOffload();
+    }
+
     /* Return the Wifi Connection statistics object */
     public WifiConnectionStatistics getConnectionStatistics() {
         enforceAccessPermission();
@@ -1851,4 +1933,162 @@
             return null;
         }
     }
+
+    public void factoryReset() {
+        enforceConnectivityInternalPermission();
+
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
+            return;
+        }
+
+        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+            // Turn mobile hotspot off
+            setWifiApEnabled(null, false);
+        }
+
+        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) {
+            // Enable wifi
+            setWifiEnabled(true);
+            // Delete all Wifi SSIDs
+            List<WifiConfiguration> networks = getConfiguredNetworks();
+            if (networks != null) {
+                for (WifiConfiguration config : networks) {
+                    removeNetwork(config.networkId);
+                }
+                saveConfiguration();
+            }
+        }
+    }
+
+    /* private methods */
+    static boolean logAndReturnFalse(String s) {
+        Log.d(TAG, s);
+        return false;
+    }
+
+    public static boolean isValid(WifiConfiguration config) {
+        String validity = checkValidity(config);
+        return validity == null || logAndReturnFalse(validity);
+    }
+
+    public static boolean isValidPasspoint(WifiConfiguration config) {
+        String validity = checkPasspointValidity(config);
+        return validity == null || logAndReturnFalse(validity);
+    }
+
+    public static String checkValidity(WifiConfiguration config) {
+        if (config.allowedKeyManagement == null)
+            return "allowed kmgmt";
+
+        if (config.allowedKeyManagement.cardinality() > 1) {
+            if (config.allowedKeyManagement.cardinality() != 2) {
+                return "cardinality != 2";
+            }
+            if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+                return "not WPA_EAP";
+            }
+            if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X))
+                    && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) {
+                return "not PSK or 8021X";
+            }
+        }
+        return null;
+    }
+
+    public static String checkPasspointValidity(WifiConfiguration config) {
+        if (!TextUtils.isEmpty(config.FQDN)) {
+            /* this is passpoint configuration; it must not have an SSID */
+            if (!TextUtils.isEmpty(config.SSID)) {
+                return "SSID not expected for Passpoint: '" + config.SSID +
+                        "' FQDN " + toHexString(config.FQDN);
+            }
+            /* this is passpoint configuration; it must have a providerFriendlyName */
+            if (TextUtils.isEmpty(config.providerFriendlyName)) {
+                return "no provider friendly name";
+            }
+            WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+            /* this is passpoint configuration; it must have enterprise config */
+            if (enterpriseConfig == null
+                    || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
+                return "no enterprise config";
+            }
+            if ((enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
+                    enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS ||
+                    enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) &&
+                    enterpriseConfig.getCaCertificate() == null) {
+                return "no CA certificate";
+            }
+        }
+        return null;
+    }
+
+    public Network getCurrentNetwork() {
+        enforceAccessPermission();
+        return mWifiStateMachine.getCurrentNetwork();
+    }
+
+    public static String toHexString(String s) {
+        if (s == null) {
+            return "null";
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append('\'').append(s).append('\'');
+        for (int n = 0; n < s.length(); n++) {
+            sb.append(String.format(" %02x", s.charAt(n) & 0xffff));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION or
+     * android.Manifest.permission.ACCESS_FINE_LOCATION and a corresponding app op is allowed
+     */
+    private boolean checkCallerCanAccessScanResults(String callingPackage, int uid) {
+        if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_FINE_LOCATION, uid)
+                == PackageManager.PERMISSION_GRANTED
+                && isAppOppAllowed(AppOpsManager.OP_FINE_LOCATION, callingPackage, uid)) {
+            return true;
+        }
+
+        if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_COARSE_LOCATION, uid)
+                == PackageManager.PERMISSION_GRANTED
+                && isAppOppAllowed(AppOpsManager.OP_COARSE_LOCATION, callingPackage, uid)) {
+            return true;
+        }
+        // Enforce location permission for apps targeting M and later versions
+        boolean enforceLocationPermission = true;
+        try {
+            enforceLocationPermission = mContext.getPackageManager().getApplicationInfo(
+                    callingPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, enforce permission anyway
+        }
+        if (enforceLocationPermission) {
+            throw new SecurityException("Need ACCESS_COARSE_LOCATION or "
+                    + "ACCESS_FINE_LOCATION permission to get scan results");
+        }
+        // Pre-M apps running in the foreground should continue getting scan results
+        if (isForegroundApp(callingPackage)) {
+            return true;
+        }
+        Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION "
+                + "permission to get scan results");
+        return false;
+    }
+
+    private boolean isAppOppAllowed(int op, String callingPackage, int uid) {
+        return mAppOps.noteOp(op, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+    }
+
+    /**
+     * Return true if the specified package name is a foreground app.
+     *
+     * @param pkgName application package name.
+     */
+    private boolean isForegroundApp(String pkgName) {
+        ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+    }
+
 }
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index eaa46f8..4bc62b3 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -31,6 +31,7 @@
  */
 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
 
+import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -40,12 +41,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.DhcpResults;
+import android.net.BaseDhcpStateMachine;
 import android.net.DhcpStateMachine;
+import android.net.Network;
+import android.net.dhcp.DhcpClient;
 import android.net.InterfaceConfiguration;
+import android.net.IpReachabilityMonitor;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
@@ -58,8 +65,6 @@
 import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
 import android.net.TrafficStats;
-import android.net.wifi.BatchedScanResult;
-import android.net.wifi.BatchedScanSettings;
 import android.net.wifi.RssiPacketCountInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.ScanSettings;
@@ -71,12 +76,14 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.WpsResult;
 import android.net.wifi.WpsResult.Status;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.os.BatteryStats;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -104,6 +111,9 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.net.NetlinkTracker;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.SupplicantBridge;
+import com.android.server.wifi.hotspot2.Utils;
 import com.android.server.wifi.p2p.WifiP2pServiceImpl;
 
 import java.io.BufferedReader;
@@ -115,14 +125,15 @@
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Queue;
+import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -136,14 +147,16 @@
  *
  * @hide
  */
-public class WifiStateMachine extends StateMachine {
+public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler {
 
     private static final String NETWORKTYPE = "WIFI";
     private static final String NETWORKTYPE_UNTRUSTED = "WIFI_UT";
     private static boolean DBG = false;
     private static boolean VDBG = false;
     private static boolean VVDBG = false;
+    private static boolean USE_PAUSE_SCANS = false;
     private static boolean mLogMessages = false;
+    private static final String TAG = "WifiStateMachine";
 
     private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
 
@@ -165,8 +178,11 @@
     protected void loge(String s) {
         Log.e(getName(), s);
     }
+    protected void logd(String s) {
+        Log.d(getName(), s);
+    }
     protected void log(String s) {;
-        Log.e(getName(), s);
+        Log.d(getName(), s);
     }
 
     private WifiMonitor mWifiMonitor;
@@ -175,35 +191,29 @@
     private WifiAutoJoinController mWifiAutoJoinController;
     private INetworkManagementService mNwService;
     private ConnectivityManager mCm;
-
+    private WifiLogger mWifiLogger;
+    private WifiApConfigStore mWifiApConfigStore;
     private final boolean mP2pSupported;
     private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
     private boolean mTemporarilyDisconnectWifi = false;
     private final String mPrimaryDeviceType;
 
     /* Scan results handling */
-    private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
+    private List<ScanDetail> mScanResults = new ArrayList<>();
     private static final Pattern scanResultPattern = Pattern.compile("\t+");
     private static final int SCAN_RESULT_CACHE_SIZE = 160;
-    private final LruCache<String, ScanResult> mScanResultCache;
+    private final LruCache<NetworkDetail, ScanDetail> mScanResultCache;
     // For debug, number of known scan results that were found as part of last scan result event,
     // as well the number of scans results returned by the supplicant with that message
     private int mNumScanResultsKnown;
     private int mNumScanResultsReturned;
 
-    /* Batch scan results */
-    private final List<BatchedScanResult> mBatchedScanResults =
-            new ArrayList<BatchedScanResult>();
-    private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
-    private int mExpectedBatchedScans = 0;
-    private long mBatchedScanMinPollTime = 0;
-
     private boolean mScreenOn = false;
 
     /* Chipset supports background scan */
     private final boolean mBackgroundScanSupported;
 
-    private String mInterfaceName;
+    private final String mInterfaceName;
     /* Tethering interface could be separate from wlan interface */
     private String mTetherInterfaceName;
 
@@ -212,12 +222,66 @@
     private int mLastNetworkId; // The network Id we successfully joined
     private boolean linkDebouncing = false;
 
+    private boolean mHalBasedPnoDriverSupported = false;
+
+    // Below booleans are configurations coming from the Developper Settings
+    private boolean mEnableAssociatedNetworkSwitchingInDevSettings = true;
+    private boolean mHalBasedPnoEnableInDevSettings = false;
+
+
+    private int mHalFeatureSet = 0;
+    private static int mPnoResultFound = 0;
+
+    @Override
+    public void onPnoNetworkFound(ScanResult results[]) {
+        if (DBG) {
+            Log.e(TAG, "onPnoNetworkFound event received num = " + results.length);
+            for (int i = 0; i < results.length; i++) {
+                Log.e(TAG, results[i].toString());
+            }
+        }
+        sendMessage(CMD_PNO_NETWORK_FOUND, results.length, 0, results);
+    }
+
+    public void processPnoNetworkFound(ScanResult results[]) {
+        ScanSettings settings = new ScanSettings();
+        settings.channelSet = new ArrayList<WifiChannel>();
+        StringBuilder sb = new StringBuilder();
+        sb.append("");
+        for (int i=0; i<results.length; i++) {
+            WifiChannel channel = new WifiChannel();
+            channel.freqMHz = results[i].frequency;
+            settings.channelSet.add(channel);
+            sb.append(results[i].SSID).append(" ");
+        }
+
+        stopPnoOffload();
+
+        Log.e(TAG, "processPnoNetworkFound starting scan cnt=" + mPnoResultFound);
+        startScan(PNO_NETWORK_FOUND_SOURCE, mPnoResultFound,  settings, null);
+        mPnoResultFound ++;
+        //sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
+        int delay = 30 * 1000;
+        // reconfigure Pno after 1 minutes if we're still in disconnected state
+        sendMessageDelayed(CMD_RESTART_AUTOJOIN_OFFLOAD, delay,
+                mRestartAutoJoinOffloadCounter, " processPnoNetworkFound " + sb.toString(),
+                (long)delay);
+        mRestartAutoJoinOffloadCounter++;
+    }
+
+    public void registerNetworkDisabled(int netId) {
+        // Restart legacy PNO and autojoin offload if needed
+        sendMessage(CMD_RESTART_AUTOJOIN_OFFLOAD, 0,
+                mRestartAutoJoinOffloadCounter, " registerNetworkDisabled " + netId);
+        mRestartAutoJoinOffloadCounter++;
+    }
+
     // Testing various network disconnect cases by sending lots of spurious
     // disconnect to supplicant
     private boolean testNetworkDisconnect = false;
 
     private boolean mEnableRssiPolling = false;
-    private boolean mEnableBackgroundScan = false;
+    private boolean mLegacyPnoEnabled = false;
     private int mRssiPollToken = 0;
     /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
     * In CONNECT_MODE, the STA can scan and connect to an access point
@@ -237,15 +301,13 @@
     private static final int SET_ALLOW_UNTRUSTED_SOURCE = -4;
     private static final int ENABLE_WIFI = -5;
     public static final int DFS_RESTRICTED_SCAN_REQUEST = -6;
+    public static final int PNO_NETWORK_FOUND_SOURCE = -7;
 
     private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
     private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
     private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
     private static final String SCAN_REQUEST_TIME = "scan_request_time";
 
-    private static final String BATCHED_SETTING = "batched_settings";
-    private static final String BATCHED_WORKSOURCE = "batched_worksource";
-
     /* Tracks if state machine has received any screen state change broadcast yet.
      * We can miss one of these at boot.
      */
@@ -298,6 +360,12 @@
     private int mDriverStartToken = 0;
 
     /**
+     * Don't select new network when previous network selection is
+     * pending connection for this much time
+     */
+    private static final int CONNECT_TIMEOUT_MSEC = 3000;
+
+    /**
      * The link properties of the wifi interface.
      * Do not modify this directly; use updateLinkProperties instead.
      */
@@ -313,11 +381,13 @@
 
     private final Object mDhcpResultsLock = new Object();
     private DhcpResults mDhcpResults;
+
+    // NOTE: Do not return to clients - use #getWiFiInfoForUid(int)
     private WifiInfo mWifiInfo;
     private NetworkInfo mNetworkInfo;
     private NetworkCapabilities mNetworkCapabilities;
     private SupplicantStateTracker mSupplicantStateTracker;
-    private DhcpStateMachine mDhcpStateMachine;
+    private BaseDhcpStateMachine mDhcpStateMachine;
     private boolean mDhcpActive = false;
 
     private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
@@ -346,6 +416,9 @@
     // Used as debug to indicate which configuration last was removed
     private WifiConfiguration lastForgetConfigurationAttempt = null;
 
+    //Random used by softAP channel Selection
+    private static Random mRandom = new Random(Calendar.getInstance().getTimeInMillis());
+
     boolean isRoaming() {
         return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
                 || mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
@@ -367,12 +440,11 @@
         if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
             // Changing to ANY
             if (!mWifiConfigStore.roamOnAny) {
-                ret =  false; // Nothing to do
+                ret = false; // Nothing to do
             }
         }
         if (VDBG) {
-           loge("autoRoamSetBSSID " + bssid
-                   + " key=" + config.configKey());
+            logd("autoRoamSetBSSID " + bssid + " key=" + config.configKey());
         }
         config.autoJoinBSSID = bssid;
         mTargetRoamBSSID = bssid;
@@ -381,17 +453,71 @@
     }
 
     /**
+     * Save the UID correctly depending on if this is a new or existing network.
+     * @return true if operation is authorized, false otherwise
+     */
+    boolean recordUidIfAuthorized(WifiConfiguration config, int uid, boolean onlyAnnotate) {
+        if (!mWifiConfigStore.isNetworkConfigured(config)) {
+            config.creatorUid = uid;
+            config.creatorName = mContext.getPackageManager().getNameForUid(uid);
+        } else if (!mWifiConfigStore.canModifyNetwork(uid, config, onlyAnnotate)) {
+            return false;
+        }
+
+        config.lastUpdateUid = uid;
+        config.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
+
+        return true;
+
+    }
+
+    /**
+     * Checks to see if user has specified if the apps configuration is connectable.
+     * If the user hasn't specified we query the user and return true.
+     *
+     * @param message The message to be deferred
+     * @param netId Network id of the configuration to check against
+     * @param allowOverride If true we won't defer to the user if the uid of the message holds the
+     *                      CONFIG_OVERRIDE_PERMISSION
+     * @return True if we are waiting for user feedback or netId is invalid. False otherwise.
+     */
+    boolean deferForUserInput(Message message, int netId, boolean allowOverride){
+        final WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(netId);
+
+        // We can only evaluate saved configurations.
+        if (config == null) {
+            logd("deferForUserInput: configuration for netId=" + netId + " not stored");
+            return true;
+        }
+
+        switch (config.userApproved) {
+            case WifiConfiguration.USER_APPROVED:
+            case WifiConfiguration.USER_BANNED:
+                return false;
+            case WifiConfiguration.USER_PENDING:
+            default: // USER_UNSPECIFIED
+               /* the intention was to ask user here; but a dialog box is   *
+                * too invasive; so we are going to allow connection for now */
+                config.userApproved = WifiConfiguration.USER_APPROVED;
+                return false;
+        }
+    }
+
+    /**
      * Subset of link properties coming from netlink.
      * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
      * and domains obtained from router advertisements (RFC 6106).
      */
     private NetlinkTracker mNetlinkTracker;
 
+    private IpReachabilityMonitor mIpReachabilityMonitor;
+
     private AlarmManager mAlarmManager;
     private PendingIntent mScanIntent;
     private PendingIntent mDriverStopIntent;
-    private PendingIntent mBatchedScanIntervalIntent;
+    private PendingIntent mPnoIntent;
 
+    private int mDisconnectedPnoAlarmCount = 0;
     /* Tracks current frequency mode */
     private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
 
@@ -407,11 +533,15 @@
     private AsyncChannel mWifiP2pChannel;
     private AsyncChannel mWifiApConfigChannel;
 
+    private WifiScanner mWifiScanner;
+
     private int mConnectionRequests = 0;
     private WifiNetworkFactory mNetworkFactory;
     private UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
     private WifiNetworkAgent mNetworkAgent;
 
+    private String[] mWhiteListedSsids = null;
+
     // Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
     // We should really persist that into the networkHistory.txt file, and read it back when
     // WifiStateMachine starts up
@@ -423,87 +553,87 @@
     /* The base for wifi message types */
     static final int BASE = Protocol.BASE_WIFI;
     /* Start the supplicant */
-    static final int CMD_START_SUPPLICANT                 = BASE + 11;
+    static final int CMD_START_SUPPLICANT                               = BASE + 11;
     /* Stop the supplicant */
-    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
+    static final int CMD_STOP_SUPPLICANT                                = BASE + 12;
     /* Start the driver */
-    static final int CMD_START_DRIVER                     = BASE + 13;
+    static final int CMD_START_DRIVER                                   = BASE + 13;
     /* Stop the driver */
-    static final int CMD_STOP_DRIVER                      = BASE + 14;
+    static final int CMD_STOP_DRIVER                                    = BASE + 14;
     /* Indicates Static IP succeeded */
-    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
+    static final int CMD_STATIC_IP_SUCCESS                              = BASE + 15;
     /* Indicates Static IP failed */
-    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
+    static final int CMD_STATIC_IP_FAILURE                              = BASE + 16;
     /* Indicates supplicant stop failed */
-    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
+    static final int CMD_STOP_SUPPLICANT_FAILED                         = BASE + 17;
     /* Delayed stop to avoid shutting down driver too quick*/
-    static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
+    static final int CMD_DELAYED_STOP_DRIVER                            = BASE + 18;
     /* A delayed message sent to start driver when it fail to come up */
-    static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
+    static final int CMD_DRIVER_START_TIMED_OUT                         = BASE + 19;
 
     /* Start the soft access point */
-    static final int CMD_START_AP                         = BASE + 21;
+    static final int CMD_START_AP                                       = BASE + 21;
     /* Indicates soft ap start succeeded */
-    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
+    static final int CMD_START_AP_SUCCESS                               = BASE + 22;
     /* Indicates soft ap start failed */
-    static final int CMD_START_AP_FAILURE                 = BASE + 23;
+    static final int CMD_START_AP_FAILURE                               = BASE + 23;
     /* Stop the soft access point */
-    static final int CMD_STOP_AP                          = BASE + 24;
+    static final int CMD_STOP_AP                                        = BASE + 24;
     /* Set the soft access point configuration */
-    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
+    static final int CMD_SET_AP_CONFIG                                  = BASE + 25;
     /* Soft access point configuration set completed */
-    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
+    static final int CMD_SET_AP_CONFIG_COMPLETED                        = BASE + 26;
     /* Request the soft access point configuration */
-    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
+    static final int CMD_REQUEST_AP_CONFIG                              = BASE + 27;
     /* Response to access point configuration request */
-    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
+    static final int CMD_RESPONSE_AP_CONFIG                             = BASE + 28;
     /* Invoked when getting a tether state change notification */
-    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
+    static final int CMD_TETHER_STATE_CHANGE                            = BASE + 29;
     /* A delayed message sent to indicate tether state change failed to arrive */
-    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
+    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT                  = BASE + 30;
 
-    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
+    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE                 = BASE + 31;
 
     /* Supplicant commands */
     /* Is supplicant alive ? */
-    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
+    static final int CMD_PING_SUPPLICANT                                = BASE + 51;
     /* Add/update a network configuration */
-    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
+    static final int CMD_ADD_OR_UPDATE_NETWORK                          = BASE + 52;
     /* Delete a network */
-    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
+    static final int CMD_REMOVE_NETWORK                                 = BASE + 53;
     /* Enable a network. The device will attempt a connection to the given network. */
-    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
+    static final int CMD_ENABLE_NETWORK                                 = BASE + 54;
     /* Enable all networks */
-    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
+    static final int CMD_ENABLE_ALL_NETWORKS                            = BASE + 55;
     /* Blacklist network. De-prioritizes the given BSSID for connection. */
-    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
+    static final int CMD_BLACKLIST_NETWORK                              = BASE + 56;
     /* Clear the blacklist network list */
-    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
+    static final int CMD_CLEAR_BLACKLIST                                = BASE + 57;
     /* Save configuration */
-    static final int CMD_SAVE_CONFIG                      = BASE + 58;
+    static final int CMD_SAVE_CONFIG                                    = BASE + 58;
     /* Get configured networks */
-    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
+    static final int CMD_GET_CONFIGURED_NETWORKS                        = BASE + 59;
     /* Get available frequencies */
-    static final int CMD_GET_CAPABILITY_FREQ              = BASE + 60;
+    static final int CMD_GET_CAPABILITY_FREQ                            = BASE + 60;
     /* Get adaptors */
-    static final int CMD_GET_SUPPORTED_FEATURES           = BASE + 61;
+    static final int CMD_GET_SUPPORTED_FEATURES                         = BASE + 61;
     /* Get configured networks with real preSharedKey */
-    static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
+    static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS             = BASE + 62;
     /* Get Link Layer Stats thru HAL */
-    static final int CMD_GET_LINK_LAYER_STATS             = BASE + 63;
+    static final int CMD_GET_LINK_LAYER_STATS                           = BASE + 63;
     /* Supplicant commands after driver start*/
     /* Initiate a scan */
-    static final int CMD_START_SCAN                       = BASE + 71;
+    static final int CMD_START_SCAN                                     = BASE + 71;
     /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
-    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
+    static final int CMD_SET_OPERATIONAL_MODE                           = BASE + 72;
     /* Disconnect from a network */
-    static final int CMD_DISCONNECT                       = BASE + 73;
+    static final int CMD_DISCONNECT                                     = BASE + 73;
     /* Reconnect to a network */
-    static final int CMD_RECONNECT                        = BASE + 74;
+    static final int CMD_RECONNECT                                      = BASE + 74;
     /* Reassociate to a network */
-    static final int CMD_REASSOCIATE                      = BASE + 75;
+    static final int CMD_REASSOCIATE                                    = BASE + 75;
     /* Get Connection Statistis */
-    static final int CMD_GET_CONNECTION_STATISTICS        = BASE + 76;
+    static final int CMD_GET_CONNECTION_STATISTICS                      = BASE + 76;
 
     /* Controls suspend mode optimizations
      *
@@ -516,38 +646,66 @@
      * - turn off roaming
      * - DTIM wake up settings
      */
-    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
+    static final int CMD_SET_HIGH_PERF_MODE                             = BASE + 77;
     /* Set the country code */
-    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
+    static final int CMD_SET_COUNTRY_CODE                               = BASE + 80;
     /* Enables RSSI poll */
-    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
+    static final int CMD_ENABLE_RSSI_POLL                               = BASE + 82;
     /* RSSI poll */
-    static final int CMD_RSSI_POLL                        = BASE + 83;
+    static final int CMD_RSSI_POLL                                      = BASE + 83;
     /* Set up packet filtering */
-    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
+    static final int CMD_START_PACKET_FILTERING                         = BASE + 84;
     /* Clear packet filter */
-    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
+    static final int CMD_STOP_PACKET_FILTERING                          = BASE + 85;
     /* Enable suspend mode optimizations in the driver */
-    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
+    static final int CMD_SET_SUSPEND_OPT_ENABLED                        = BASE + 86;
     /* Delayed NETWORK_DISCONNECT */
-    static final int CMD_DELAYED_NETWORK_DISCONNECT       = BASE + 87;
+    static final int CMD_DELAYED_NETWORK_DISCONNECT                     = BASE + 87;
     /* When there are no saved networks, we do a periodic scan to notify user of
      * an open network */
-    static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
+    static final int CMD_NO_NETWORKS_PERIODIC_SCAN                      = BASE + 88;
     /* Test network Disconnection NETWORK_DISCONNECT */
-    static final int CMD_TEST_NETWORK_DISCONNECT          = BASE + 89;
+    static final int CMD_TEST_NETWORK_DISCONNECT                        = BASE + 89;
+
     private int testNetworkDisconnectCounter = 0;
 
     /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
-    static final int MULTICAST_V6  = 1;
-    static final int MULTICAST_V4  = 0;
+    static final int MULTICAST_V6 = 1;
+    static final int MULTICAST_V4 = 0;
 
-   /* Set the frequency band */
-    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
+    /* Set the frequency band */
+    static final int CMD_SET_FREQUENCY_BAND                             = BASE + 90;
     /* Enable TDLS on a specific MAC address */
-    static final int CMD_ENABLE_TDLS                      = BASE + 92;
+    static final int CMD_ENABLE_TDLS                                    = BASE + 92;
     /* DHCP/IP configuration watchdog */
-    static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER    = BASE + 93;
+    static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER            = BASE + 93;
+
+    /**
+     * Watchdog for protecting against b/16823537
+     * Leave time for 4-way handshake to succeed
+     */
+    static final int ROAM_GUARD_TIMER_MSEC = 15000;
+
+    int roamWatchdogCount = 0;
+    /* Roam state watchdog */
+    static final int CMD_ROAM_WATCHDOG_TIMER                            = BASE + 94;
+    /* Screen change intent handling */
+    static final int CMD_SCREEN_STATE_CHANGED                           = BASE + 95;
+
+    /* Disconnecting state watchdog */
+    static final int CMD_DISCONNECTING_WATCHDOG_TIMER                   = BASE + 96;
+
+    /* Remove a packages associated configrations */
+    static final int CMD_REMOVE_APP_CONFIGURATIONS                      = BASE + 97;
+
+    /* Disable an ephemeral network */
+    static final int CMD_DISABLE_EPHEMERAL_NETWORK                      = BASE + 98;
+
+    /* Get matching network */
+    static final int CMD_GET_MATCHING_CONFIG                            = BASE + 99;
+
+    /* alert from firmware */
+    static final int CMD_FIRMWARE_ALERT                                 = BASE + 100;
 
     /**
      * Make this timer 40 seconds, which is about the normal DHCP timeout.
@@ -560,84 +718,82 @@
 
     /* Commands from/to the SupplicantStateTracker */
     /* Reset the supplicant state tracker */
-    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
-
-
-    /**
-     * Watchdog for protecting against b/16823537
-     * Leave time for 4-ways handshake to succeed
-     */
-    static final int ROAM_GUARD_TIMER_MSEC = 15000;
-
-    int roamWatchdogCount = 0;
-    /* Roam state watchdog */
-    static final int CMD_ROAM_WATCHDOG_TIMER    = BASE + 94;
-    /* Screen change intent handling */
-    static final int CMD_SCREEN_STATE_CHANGED              = BASE + 95;
+    static final int CMD_RESET_SUPPLICANT_STATE                         = BASE + 111;
 
     int disconnectingWatchdogCount = 0;
     static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
 
-    /* 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;
+    public static final int CMD_ENABLE_P2P                              = BASE + 131;
     /* In order to shut down supplicant cleanly, we wait till p2p has
      * been disabled */
-    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
-    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
+    public static final int CMD_DISABLE_P2P_REQ                         = BASE + 132;
+    public static final int CMD_DISABLE_P2P_RSP                         = BASE + 133;
 
-    public static final int CMD_BOOT_COMPLETED            = BASE + 134;
-
-    /* change the batch scan settings.
-     * arg1 = responsible UID
-     * arg2 = csph (channel scans per hour)
-     * obj = bundle with the new settings and the optional worksource
-     */
-    public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
-    public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
-    public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
+    public static final int CMD_BOOT_COMPLETED                          = BASE + 134;
 
     /* We now have a valid IP configuration. */
-    static final int CMD_IP_CONFIGURATION_SUCCESSFUL      = BASE + 138;
+    static final int CMD_IP_CONFIGURATION_SUCCESSFUL                    = BASE + 138;
     /* We no longer have a valid IP configuration. */
-    static final int CMD_IP_CONFIGURATION_LOST            = BASE + 139;
+    static final int CMD_IP_CONFIGURATION_LOST                          = BASE + 139;
     /* Link configuration (IP address, DNS, ...) changes notified via netlink */
-    static final int CMD_UPDATE_LINKPROPERTIES            = BASE + 140;
+    static final int CMD_UPDATE_LINKPROPERTIES                          = BASE + 140;
 
     /* Supplicant is trying to associate to a given BSSID */
-    static final int CMD_TARGET_BSSID                     = BASE + 141;
+    static final int CMD_TARGET_BSSID                                   = BASE + 141;
 
     /* Reload all networks and reconnect */
-    static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
+    static final int CMD_RELOAD_TLS_AND_RECONNECT                       = BASE + 142;
 
-    static final int CMD_AUTO_CONNECT                     = BASE + 143;
+    static final int CMD_AUTO_CONNECT                                   = BASE + 143;
 
-    static final int network_status_unwanted_disconnect = 0;
-    static final int network_status_unwanted_disable_autojoin = 1;
+    private static final int NETWORK_STATUS_UNWANTED_DISCONNECT         = 0;
+    private static final int NETWORK_STATUS_UNWANTED_VALIDATION_FAILED  = 1;
+    private static final int NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN   = 2;
 
-    static final int CMD_UNWANTED_NETWORK                 = BASE + 144;
+    static final int CMD_UNWANTED_NETWORK                               = BASE + 144;
 
-    static final int CMD_AUTO_ROAM                        = BASE + 145;
+    static final int CMD_AUTO_ROAM                                      = BASE + 145;
 
-    static final int CMD_AUTO_SAVE_NETWORK                = BASE + 146;
+    static final int CMD_AUTO_SAVE_NETWORK                              = BASE + 146;
 
-    static final int CMD_ASSOCIATED_BSSID                = BASE + 147;
+    static final int CMD_ASSOCIATED_BSSID                               = BASE + 147;
 
-    static final int CMD_NETWORK_STATUS                  = BASE + 148;
+    static final int CMD_NETWORK_STATUS                                 = BASE + 148;
+
+    /* A layer 3 neighbor on the Wi-Fi link became unreachable. */
+    static final int CMD_IP_REACHABILITY_LOST                           = BASE + 149;
+
+    /* Remove a packages associated configrations */
+    static final int CMD_REMOVE_USER_CONFIGURATIONS                     = BASE + 152;
+
+    static final int CMD_ACCEPT_UNVALIDATED                             = BASE + 153;
+
+    /* used to restart PNO when it was stopped due to association attempt */
+    static final int CMD_RESTART_AUTOJOIN_OFFLOAD                       = BASE + 154;
+
+    static int mRestartAutoJoinOffloadCounter = 0;
+
+    /* used to log if PNO was started */
+    static final int CMD_STARTED_PNO_DBG                                = BASE + 155;
+
+    static final int CMD_PNO_NETWORK_FOUND                              = BASE + 156;
+
+    /* used to log if PNO was started */
+    static final int CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION              = BASE + 158;
+
+    /* used to log if GSCAN was started */
+    static final int CMD_STARTED_GSCAN_DBG                              = BASE + 159;
+
 
     /* Wifi state machine modes of operation */
     /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
-    public static final int CONNECT_MODE                   = 1;
+    public static final int CONNECT_MODE = 1;
     /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
-    public static final int SCAN_ONLY_MODE                 = 2;
+    public static final int SCAN_ONLY_MODE = 2;
     /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
-    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
+    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
 
     private static final int SUCCESS = 1;
     private static final int FAILURE = -1;
@@ -649,9 +805,9 @@
      */
     private int mSuspendOptNeedsDisabled = 0;
 
-    private static final int SUSPEND_DUE_TO_DHCP       = 1;
-    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
-    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
+    private static final int SUSPEND_DUE_TO_DHCP = 1;
+    private static final int SUSPEND_DUE_TO_HIGH_PERF = 1 << 1;
+    private static final int SUSPEND_DUE_TO_SCREEN = 1 << 2;
 
     /* Tracks if user has enabled suspend optimizations through settings */
     private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
@@ -665,7 +821,11 @@
      */
     private final int mDefaultFrameworkScanIntervalMs;
 
-    private final int mDisconnectedScanPeriodMs;
+
+    /**
+     * Scan period for the NO_NETWORKS_PERIIDOC_SCAN_FEATURE
+     */
+    private final int mNoNetworksPeriodicScan;
 
     /**
      * Supplicant scan interval in milliseconds.
@@ -690,7 +850,7 @@
      * autojoin while connected with screen lit
      * Max time is 5 minutes
      */
-    private static final long  maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
+    private static final long maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
 
     /**
      * Minimum time interval between enabling all networks.
@@ -710,13 +870,14 @@
     private int mDelayedStopCounter;
     private boolean mInDelayedStop = false;
 
-    // sometimes telephony gives us this data before boot is complete and we can't store it
-    // until after, so the write is deferred
-    private volatile String mPersistedCountryCode;
+    // there is a delay between StateMachine change country code and Supplicant change country code
+    // here save the current WifiStateMachine set country code
+    private volatile String mSetCountryCode = null;
 
     // Supplicant doesn't like setting the same country code multiple times (it may drop
-    // currently connected network), so we save the country code here to avoid redundency
-    private String mLastSetCountryCode;
+    // currently connected network), so we save the current device set country code here to avoid
+    // redundency
+    private String mDriverSetCountryCode = null;
 
     /* Default parent state */
     private State mDefaultState = new DefaultState();
@@ -773,9 +934,40 @@
     /* Waiting for untether confirmation before stopping soft Ap */
     private State mUntetheringState = new UntetheringState();
 
+
+
+    private class WifiScanListener implements WifiScanner.ScanListener {
+        @Override
+        public void onSuccess() {
+            Log.e(TAG, "WifiScanListener onSuccess");
+        };
+        @Override
+        public void onFailure(int reason, String description) {
+            Log.e(TAG, "WifiScanListener onFailure");
+        };
+        @Override
+        public void onPeriodChanged(int periodInMs) {
+            Log.e(TAG, "WifiScanListener onPeriodChanged  period=" + periodInMs);
+        }
+        @Override
+        public void onResults(WifiScanner.ScanData[] results) {
+            Log.e(TAG, "WifiScanListener onResults2 "  + results.length);
+        }
+        @Override
+        public void onFullResult(ScanResult fullScanResult) {
+            Log.e(TAG, "WifiScanListener onFullResult " + fullScanResult.toString());
+        }
+
+        WifiScanListener() {}
+    }
+
+    WifiScanListener mWifiScanListener = new WifiScanListener();
+
+
     private class TetherStateChange {
         ArrayList<String> available;
         ArrayList<String> active;
+
         TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
             available = av;
             active = ac;
@@ -786,50 +978,42 @@
         int networkId;
         int protocol;
         String ssid;
-        String[] challenges;
-    }
-
-    public static class SimAuthResponseData {
-        int id;
-        String Kc1;
-        String SRES1;
-        String Kc2;
-        String SRES2;
-        String Kc3;
-        String SRES3;
+        // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
+        // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
+        String[] data;
     }
 
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
-     *         {@link WifiManager#WIFI_STATE_DISABLING},
-     *         {@link WifiManager#WIFI_STATE_ENABLED},
-     *         {@link WifiManager#WIFI_STATE_ENABLING},
-     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
-     *
+     * {@link WifiManager#WIFI_STATE_DISABLING},
+     * {@link WifiManager#WIFI_STATE_ENABLED},
+     * {@link WifiManager#WIFI_STATE_ENABLING},
+     * {@link WifiManager#WIFI_STATE_UNKNOWN}
      */
     private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
 
     /**
      * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
-     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
-     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
-     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
-     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
-     *
+     * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+     * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+     * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+     * {@link WifiManager#WIFI_AP_STATE_FAILED}
      */
     private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
 
     private static final int SCAN_REQUEST = 0;
     private static final String ACTION_START_SCAN =
-        "com.android.server.WifiManager.action.START_SCAN";
+            "com.android.server.WifiManager.action.START_SCAN";
+
+    private static final int PNO_START_REQUEST = 0;
+    private static final String ACTION_START_PNO =
+            "com.android.server.WifiManager.action.START_PNO";
 
     private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
     private static final int DRIVER_STOP_REQUEST = 0;
     private static final String ACTION_DELAYED_DRIVER_STOP =
-        "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
+            "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
 
-    private static final String ACTION_REFRESH_BATCHED_SCAN =
-            "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
     /**
      * Keep track of whether WIFI is running.
      */
@@ -852,17 +1036,6 @@
 
     private final IBatteryStats mBatteryStats;
 
-    private BatchedScanSettings mBatchedScanSettings = null;
-
-    /**
-     * Track the worksource/cost of the current settings and track what's been noted
-     * to the battery stats, so we can mark the end of the previous when changing.
-     */
-    private WorkSource mBatchedScanWorkSource = null;
-    private int mBatchedScanCsph = 0;
-    private WorkSource mNotedBatchedScanWorkSource = null;
-    private int mNotedBatchedScanCsph = 0;
-
     private String mTcpBufferSizes = null;
 
     // Used for debug and stats gathering
@@ -870,10 +1043,17 @@
 
     final static int frameworkMinScanIntervalSaneValue = 10000;
 
+    boolean mPnoEnabled;
+    boolean mLazyRoamEnabled;
+    long mGScanStartTimeMilli;
+    long mGScanPeriodMilli;
+
     public WifiStateMachine(Context context, String wlanInterface,
-            WifiTrafficPoller trafficPoller){
+                            WifiTrafficPoller trafficPoller) {
         super("WifiStateMachine");
         mContext = context;
+        mSetCountryCode = Settings.Global.getString(
+                mContext.getContentResolver(), Settings.Global.WIFI_COUNTRY_CODE);
         mInterfaceName = wlanInterface;
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -886,17 +1066,21 @@
                 PackageManager.FEATURE_WIFI_DIRECT);
 
         mWifiNative = new WifiNative(mInterfaceName);
-        mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
+        mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);
         mWifiAutoJoinController = new WifiAutoJoinController(context, this,
                 mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
         mWifiMonitor = new WifiMonitor(this, mWifiNative);
+        mWifiLogger = new WifiLogger(this);
+
         mWifiInfo = new WifiInfo();
         mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
                 getHandler());
         mLinkProperties = new LinkProperties();
 
         IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
-        mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
+        mWifiP2pServiceImpl = (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
+
+        IBinder s2 = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
 
         mNetworkInfo.setIsAvailable(false);
         mLastBssid = null;
@@ -914,9 +1098,9 @@
             loge("Couldn't register netlink tracker: " + e.toString());
         }
 
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mScanIntent = getPrivateBroadcast(ACTION_START_SCAN, SCAN_REQUEST);
-        mBatchedScanIntervalIntent = getPrivateBroadcast(ACTION_REFRESH_BATCHED_SCAN, 0);
+        mPnoIntent = getPrivateBroadcast(ACTION_START_PNO, PNO_START_REQUEST);
 
         // Make sure the interval is not configured less than 10 seconds
         int period = mContext.getResources().getInteger(
@@ -925,8 +1109,10 @@
             period = frameworkMinScanIntervalSaneValue;
         }
         mDefaultFrameworkScanIntervalMs = period;
-        mDisconnectedScanPeriodMs = mContext.getResources().getInteger(
-                R.integer.config_wifi_disconnected_scan_interval);
+
+        mNoNetworksPeriodicScan = mContext.getResources().getInteger(
+                R.integer.config_wifi_no_network_periodic_scan_interval);
+
         mDriverStopDelayMs = mContext.getResources().getInteger(
                 R.integer.config_wifi_driver_stop_delay);
 
@@ -937,7 +1123,7 @@
                 R.string.config_wifi_p2p_device_type);
 
         mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
+                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
 
         mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
@@ -948,16 +1134,16 @@
         mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
 
         mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    ArrayList<String> available = intent.getStringArrayListExtra(
-                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-                    ArrayList<String> active = intent.getStringArrayListExtra(
-                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
-                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
-                }
-            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        ArrayList<String> available = intent.getStringArrayListExtra(
+                                ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                        ArrayList<String> active = intent.getStringArrayListExtra(
+                                ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                        sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
+                    }
+                }, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
 
         mContext.registerReceiver(
                 new BroadcastReceiver() {
@@ -966,15 +1152,27 @@
                         sScanAlarmIntentCount++; // Used for debug only
                         startScan(SCAN_ALARM_SOURCE, mDelayedScanCounter.incrementAndGet(), null, null);
                         if (VDBG)
-                            loge("WiFiStateMachine SCAN ALARM -> " + mDelayedScanCounter.get());
+                            logd("SCAN ALARM -> " + mDelayedScanCounter.get());
                     }
                 },
                 new IntentFilter(ACTION_START_SCAN));
 
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        sendMessage(CMD_RESTART_AUTOJOIN_OFFLOAD, 0,
+                                mRestartAutoJoinOffloadCounter, "pno alarm");
+                        if (DBG)
+                            logd("PNO START ALARM sent");
+                    }
+                },
+                new IntentFilter(ACTION_START_PNO));
+
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
@@ -985,8 +1183,6 @@
                             sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
                         } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                             sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
-                        } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
-                            startNextBatchedScanAsync();
                         }
                     }
                 }, filter);
@@ -995,14 +1191,14 @@
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                       int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
-                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
+                        int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
+                        sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
                     }
                 },
                 new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
 
         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
-                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
+                        Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
                 new ContentObserver(getHandler()) {
                     @Override
                     public void onChange(boolean selfChange) {
@@ -1020,9 +1216,9 @@
                 },
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
 
-        mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
+        mScanResultCache = new LruCache<>(SCAN_RESULT_CACHE_SIZE);
 
-        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
 
         mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
@@ -1076,7 +1272,8 @@
     PendingIntent getPrivateBroadcast(String action, int requestCode) {
         Intent intent = new Intent(action, null);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.setPackage(this.getClass().getPackage().getName());
+        //intent.setPackage(this.getClass().getPackage().getName());
+        intent.setPackage("android");
         return PendingIntent.getBroadcast(mContext, requestCode, intent, 0);
     }
 
@@ -1101,6 +1298,7 @@
             mLogMessages = false;
             mWifiNative.setSupplicantLogLevel("INFO");
         }
+        mWifiLogger.startLogging(mVerboseLoggingLevel > 0);
         mWifiAutoJoinController.enableVerboseLogging(verbose);
         mWifiMonitor.enableVerboseLogging(verbose);
         mWifiNative.enableVerboseLogging(verbose);
@@ -1108,6 +1306,99 @@
         mSupplicantStateTracker.enableVerboseLogging(verbose);
     }
 
+    public void setHalBasedAutojoinOffload(int enabled) {
+        // Shoult be used for debug only, triggered form developper settings
+        // enabling HAl based PNO dynamically is not safe and not a normal operation
+        mHalBasedPnoEnableInDevSettings = enabled > 0;
+        mWifiConfigStore.enableHalBasedPno.set(mHalBasedPnoEnableInDevSettings);
+        mWifiConfigStore.enableSsidWhitelist.set(mHalBasedPnoEnableInDevSettings);
+        sendMessage(CMD_DISCONNECT);
+    }
+
+    int getHalBasedAutojoinOffload() {
+        return mHalBasedPnoEnableInDevSettings ? 1 : 0;
+    }
+
+    boolean useHalBasedAutoJoinOffload() {
+        // all three settings need to be true:
+        // - developper settings switch
+        // - driver support
+        // - config option
+        return mHalBasedPnoEnableInDevSettings
+                && mHalBasedPnoDriverSupported
+                && mWifiConfigStore.enableHalBasedPno.get();
+    }
+
+    boolean allowFullBandScanAndAssociated() {
+
+        if (!getEnableAutoJoinWhenAssociated()) {
+            if (DBG) {
+                Log.e(TAG, "allowFullBandScanAndAssociated: "
+                        + " enableAutoJoinWhenAssociated : disallow");
+            }
+            return false;
+        }
+
+        if (mWifiInfo.txSuccessRate >
+                mWifiConfigStore.maxTxPacketForFullScans
+                || mWifiInfo.rxSuccessRate >
+                mWifiConfigStore.maxRxPacketForFullScans) {
+            if (DBG) {
+                Log.e(TAG, "allowFullBandScanAndAssociated: packet rate tx"
+                        + mWifiInfo.txSuccessRate + "  rx "
+                        + mWifiInfo.rxSuccessRate
+                        + " allow scan with traffic " + getAllowScansWithTraffic());
+            }
+            // Too much traffic at the interface, hence no full band scan
+            if (getAllowScansWithTraffic() == 0) {
+                return false;
+            }
+        }
+
+        if (getCurrentState() != mConnectedState) {
+            if (DBG) {
+                Log.e(TAG, "allowFullBandScanAndAssociated: getCurrentState() : disallow");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    long mLastScanPermissionUpdate = 0;
+    boolean mConnectedModeGScanOffloadStarted = false;
+    // Don't do a G-scan enable/re-enable cycle more than once within 20seconds
+    // The function updateAssociatedScanPermission() can be called quite frequently, hence
+    // we want to throttle the GScan Stop->Start transition
+    static final long SCAN_PERMISSION_UPDATE_THROTTLE_MILLI = 20000;
+    void updateAssociatedScanPermission() {
+
+        if (useHalBasedAutoJoinOffload()) {
+            boolean allowed = allowFullBandScanAndAssociated();
+
+            long now = System.currentTimeMillis();
+            if (mConnectedModeGScanOffloadStarted && !allowed) {
+                if (DBG) {
+                    Log.e(TAG, " useHalBasedAutoJoinOffload stop offload");
+                }
+                stopPnoOffload();
+                stopGScan(" useHalBasedAutoJoinOffload");
+            }
+            if (!mConnectedModeGScanOffloadStarted && allowed) {
+                if ((now - mLastScanPermissionUpdate) > SCAN_PERMISSION_UPDATE_THROTTLE_MILLI) {
+                    // Re-enable Gscan offload, this will trigger periodic scans and allow firmware
+                    // to look for 5GHz BSSIDs and better networks
+                    if (DBG) {
+                        Log.e(TAG, " useHalBasedAutoJoinOffload restart offload");
+                    }
+                    startGScanConnectedModeOffload("updatePermission "
+                            + (now - mLastScanPermissionUpdate) + "ms");
+                    mLastScanPermissionUpdate = now;
+                }
+            }
+        }
+    }
+
     private int mAggressiveHandover = 0;
 
     int getAggressiveHandover() {
@@ -1118,31 +1409,46 @@
         mAggressiveHandover = enabled;
     }
 
+    public void clearANQPCache() {
+        mWifiConfigStore.trimANQPCache(true);
+    }
+
     public void setAllowScansWithTraffic(int enabled) {
-        mWifiConfigStore.alwaysEnableScansWhileAssociated = enabled;
+        mWifiConfigStore.alwaysEnableScansWhileAssociated.set(enabled);
     }
 
     public int getAllowScansWithTraffic() {
-        return mWifiConfigStore.alwaysEnableScansWhileAssociated;
+        return mWifiConfigStore.alwaysEnableScansWhileAssociated.get();
     }
 
+    public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+        boolean old_state = getEnableAutoJoinWhenAssociated();
+        mWifiConfigStore.enableAutoJoinWhenAssociated.set(enabled);
+        if (!old_state && enabled && mScreenOn && getCurrentState() == mConnectedState) {
+            startDelayedScan(mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get(), null,
+                    null);
+        }
+        return true;
+    }
+
+    public boolean getEnableAutoJoinWhenAssociated() {
+        return mWifiConfigStore.enableAutoJoinWhenAssociated.get();
+    }
     /*
      *
      * Framework scan control
      */
 
     private boolean mAlarmEnabled = false;
-    /* This is set from the overlay config file or from a secure setting.
-     * A value of 0 disables scanning in the framework.
-     */
-    private long mFrameworkScanIntervalMs = 10000;
 
     private AtomicInteger mDelayedScanCounter = new AtomicInteger();
 
     private void setScanAlarm(boolean enabled) {
         if (PDBG) {
-            loge("setScanAlarm " + enabled
-                    + " period " + mDefaultFrameworkScanIntervalMs
+            String state;
+            if (enabled) state = "enabled"; else state = "disabled";
+            logd("setScanAlarm " + state
+                    + " defaultperiod " + mDefaultFrameworkScanIntervalMs
                     + " mBackgroundScanSupported " + mBackgroundScanSupported);
         }
         if (mBackgroundScanSupported == false) {
@@ -1168,11 +1474,11 @@
 
     private void cancelDelayedScan() {
         mDelayedScanCounter.incrementAndGet();
-        loge("cancelDelayedScan -> " + mDelayedScanCounter);
     }
 
     private boolean checkAndRestartDelayedScan(int counter, boolean restart, int milli,
-                                   ScanSettings settings, WorkSource workSource) {
+                                               ScanSettings settings, WorkSource workSource) {
+
         if (counter != mDelayedScanCounter.get()) {
             return false;
         }
@@ -1194,21 +1500,21 @@
         mDelayedScanCounter.incrementAndGet();
         if (mScreenOn &&
                 (getCurrentState() == mDisconnectedState
-                || getCurrentState() == mConnectedState)) {
+                        || getCurrentState() == mConnectedState)) {
             Bundle bundle = new Bundle();
             bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
             bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
             bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
             sendMessageDelayed(CMD_START_SCAN, SCAN_ALARM_SOURCE,
                     mDelayedScanCounter.get(), bundle, milli);
-            if (DBG) loge("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
+            if (DBG) logd("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
         } else if (mBackgroundScanSupported == false
                 && !mScreenOn && getCurrentState() == mDisconnectedState) {
             setScanAlarm(true);
-            if (DBG) loge("startDelayedScan start scan alarm -> "
+            if (DBG) logd("startDelayedScan start scan alarm -> "
                     + mDelayedScanCounter + " milli " + milli);
         } else {
-            if (DBG) loge("startDelayedScan unhandled -> "
+            if (DBG) logd("startDelayedScan unhandled -> "
                     + mDelayedScanCounter + " milli " + milli);
         }
     }
@@ -1226,9 +1532,11 @@