Merge "Add OEM specific error code space which can by used to replace Generic errors." into mm-wireless-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index a8671d4..ca92c45 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20666,12 +20666,18 @@
   }
 
   public class RttManager {
+    method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
+    method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
     method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
     method public android.net.wifi.RttManager.RttCapabilities getRttCapabilities();
     method public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener);
     method public void stopRanging(android.net.wifi.RttManager.RttListener);
     field public static final int BASE = 160256; // 0x27200
     field public static final int CMD_OP_ABORTED = 160260; // 0x27204
+    field public static final int CMD_OP_DISABLE_RESPONDER = 160262; // 0x27206
+    field public static final int CMD_OP_ENABLE_RESPONDER = 160261; // 0x27205
+    field public static final int CMD_OP_ENALBE_RESPONDER_FAILED = 160264; // 0x27208
+    field public static final int CMD_OP_ENALBE_RESPONDER_SUCCEEDED = 160263; // 0x27207
     field public static final int CMD_OP_FAILED = 160258; // 0x27202
     field public static final int CMD_OP_START_RANGING = 160256; // 0x27200
     field public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201
@@ -20680,6 +20686,7 @@
     field public static final int PREAMBLE_HT = 2; // 0x2
     field public static final int PREAMBLE_LEGACY = 1; // 0x1
     field public static final int PREAMBLE_VHT = 4; // 0x4
+    field public static final int REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; // 0xfffffffa
     field public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd
     field public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc
     field public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe
@@ -20747,6 +20754,25 @@
     field public android.net.wifi.RttManager.RttResult[] mResults;
   }
 
+  public static abstract class RttManager.ResponderCallback {
+    ctor public RttManager.ResponderCallback();
+    method public abstract void onResponderEnableFailure(int);
+    method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig);
+  }
+
+  public static class RttManager.ResponderConfig implements android.os.Parcelable {
+    ctor public RttManager.ResponderConfig();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.RttManager.ResponderConfig> CREATOR;
+    field public int centerFreq0;
+    field public int centerFreq1;
+    field public int channelWidth;
+    field public int frequency;
+    field public java.lang.String macAddress;
+    field public int preamble;
+  }
+
   public static class RttManager.RttCapabilities implements android.os.Parcelable {
     ctor public RttManager.RttCapabilities();
     method public int describeContents();
@@ -20756,6 +20782,7 @@
     field public boolean lcrSupported;
     field public boolean oneSidedRttSupported;
     field public int preambleSupported;
+    field public boolean responderSupported;
     field public deprecated boolean supportedPeerType;
     field public deprecated boolean supportedType;
     field public boolean twoSided11McRttSupported;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index e88bc26..9870e7b 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -20,7 +20,12 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.BackupUtils;
 
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Objects;
 
 /**
@@ -30,6 +35,11 @@
  * @hide
  */
 public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
+    /**
+     * Current Version of the Backup Serializer.
+     */
+    private static final int BACKUP_VERSION = 1;
+
     public static final int CYCLE_NONE = -1;
     public static final long WARNING_DISABLED = -1;
     public static final long LIMIT_DISABLED = -1;
@@ -191,4 +201,41 @@
             return new NetworkPolicy[size];
         }
     };
+
+    public byte[] getBytesForBackup() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(baos);
+
+        out.writeInt(BACKUP_VERSION);
+        out.write(template.getBytesForBackup());
+        out.writeInt(cycleDay);
+        BackupUtils.writeString(out, cycleTimezone);
+        out.writeLong(warningBytes);
+        out.writeLong(limitBytes);
+        out.writeLong(lastWarningSnooze);
+        out.writeLong(lastLimitSnooze);
+        out.writeInt(metered ? 1 : 0);
+        out.writeInt(inferred ? 1 : 0);
+        return baos.toByteArray();
+    }
+
+    public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
+            BackupUtils.BadVersionException {
+        int version = in.readInt();
+        if (version < 1 || version > BACKUP_VERSION) {
+            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
+        }
+
+        NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+        int cycleDay = in.readInt();
+        String cycleTimeZone = BackupUtils.readString(in);
+        long warningBytes = in.readLong();
+        long limitBytes = in.readLong();
+        long lastWarningSnooze = in.readLong();
+        long lastLimitSnooze = in.readLong();
+        boolean metered = in.readInt() == 1;
+        boolean inferred = in.readInt() == 1;
+        return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes, limitBytes,
+                lastWarningSnooze, lastLimitSnooze, metered, inferred);
+    }
 }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b7a411e..5761d66 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -28,15 +28,21 @@
 import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
 import static android.telephony.TelephonyManager.getNetworkClass;
+
 import static com.android.internal.util.ArrayUtils.contains;
 
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.BackupUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -47,6 +53,10 @@
  * @hide
  */
 public class NetworkTemplate implements Parcelable {
+    /**
+     * Current Version of the Backup Serializer.
+     */
+    private static final int BACKUP_VERSION = 1;
 
     public static final int MATCH_MOBILE_ALL = 1;
     @Deprecated
@@ -443,4 +453,31 @@
             return new NetworkTemplate[size];
         }
     };
+
+    public byte[] getBytesForBackup() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(baos);
+
+        out.writeInt(BACKUP_VERSION);
+
+        out.writeInt(mMatchRule);
+        BackupUtils.writeString(out, mSubscriberId);
+        BackupUtils.writeString(out, mNetworkId);
+
+        return baos.toByteArray();
+    }
+
+    public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
+            throws IOException, BackupUtils.BadVersionException {
+        int version = in.readInt();
+        if (version < 1 || version > BACKUP_VERSION) {
+            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
+        }
+
+        int matchRule = in.readInt();
+        String subscriberId = BackupUtils.readString(in);
+        String networkId = BackupUtils.readString(in);
+
+        return new NetworkTemplate(matchRule, subscriberId, networkId);
+    }
 }
diff --git a/core/java/android/util/BackupUtils.java b/core/java/android/util/BackupUtils.java
new file mode 100644
index 0000000..474ceda
--- /dev/null
+++ b/core/java/android/util/BackupUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 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 android.util;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Utility methods for Backup/Restore
+ * @hide
+ */
+public class BackupUtils {
+
+    public static final int NULL = 0;
+    public static final int NOT_NULL = 1;
+
+    /**
+     * Thrown when there is a backup version mismatch
+     * between the data received and what the system can handle
+     */
+    public static class BadVersionException extends Exception {
+        public BadVersionException(String message) {
+            super(message);
+        }
+    }
+
+    public static String readString(DataInputStream in) throws IOException {
+        return (in.readByte() == NOT_NULL) ? in.readUTF() : null;
+    }
+
+    public static void writeString(DataOutputStream out, String val) throws IOException {
+        if (val != null) {
+            out.writeByte(NOT_NULL);
+            out.writeUTF(val);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2e96f18..32df06c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -24,6 +24,8 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.Uri;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -34,6 +36,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.BackupUtils;
 import android.util.Log;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -81,36 +84,49 @@
     private static final String KEY_GLOBAL = "global";
     private static final String KEY_LOCALE = "locale";
     private static final String KEY_LOCK_SETTINGS = "lock_settings";
+    private static final String KEY_SOFTAP_CONFIG = "softap_config";
+    private static final String KEY_NETWORK_POLICIES = "network_policies";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 4;
+    private static final int STATE_VERSION = 6;
+
+    // Versioning of the Network Policies backup payload.
+    private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
+
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
-    private static final int STATE_SYSTEM          = 0;
-    private static final int STATE_SECURE          = 1;
-    private static final int STATE_LOCALE          = 2;
-    private static final int STATE_WIFI_SUPPLICANT = 3;
-    private static final int STATE_WIFI_CONFIG     = 4;
-    private static final int STATE_GLOBAL          = 5;
-    private static final int STATE_LOCK_SETTINGS   = 6;
+    private static final int STATE_SYSTEM           = 0;
+    private static final int STATE_SECURE           = 1;
+    private static final int STATE_LOCALE           = 2;
+    private static final int STATE_WIFI_SUPPLICANT  = 3;
+    private static final int STATE_WIFI_CONFIG      = 4;
+    private static final int STATE_GLOBAL           = 5;
+    private static final int STATE_LOCK_SETTINGS    = 6;
+    private static final int STATE_SOFTAP_CONFIG    = 7;
+    private static final int STATE_NETWORK_POLICIES = 8;
 
-    private static final int STATE_SIZE            = 7; // The current number of state items
+    private static final int STATE_SIZE             = 9; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
-        0,
-        4,              // version 1
-        5,              // version 2 added STATE_WIFI_CONFIG
-        6,              // version 3 added STATE_GLOBAL
-        STATE_SIZE      // version 4 added STATE_LOCK_SETTINGS
+            0,
+            4,              // version 1
+            5,              // version 2 added STATE_WIFI_CONFIG
+            6,              // version 3 added STATE_GLOBAL
+            7,              // version 4 added STATE_LOCK_SETTINGS
+            8,              // version 5 added STATE_SOFTAP_CONFIG
+            STATE_SIZE      // version 6 added STATE_NETWORK_POLICIES
     };
 
     // Versioning of the 'full backup' format
-    private static final int FULL_BACKUP_VERSION = 3;
+    // Increment this version any time a new item is added
+    private static final int FULL_BACKUP_VERSION = 5;
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
+    private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
+    private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
 
     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
 
@@ -119,8 +135,8 @@
     private static final String TAG = "SettingsBackupAgent";
 
     private static final String[] PROJECTION = {
-        Settings.NameValueTable.NAME,
-        Settings.NameValueTable.VALUE
+            Settings.NameValueTable.NAME,
+            Settings.NameValueTable.VALUE
     };
 
     private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
@@ -146,7 +162,7 @@
 
     private SettingsHelper mSettingsHelper;
     private WifiManager mWfm;
-    private static String mWifiConfigFile;
+    private String mWifiConfigFile;
 
     // Chain of asynchronous operations used when rewriting the wifi supplicant config file
     WifiDisableRunnable mWifiDisable = null;
@@ -338,7 +354,7 @@
                                 }
                                 continue;
                             }
-                            if (! mKnownNetworks.contains(net)) {
+                            if (!mKnownNetworks.contains(net)) {
                                 if (DEBUG_BACKUP) {
                                     Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
                                 }
@@ -405,26 +421,34 @@
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
+        byte[] softApConfigData = getSoftAPConfiguration();
+        byte[] netPoliciesData = getNetworkPolicies();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
         stateChecksums[STATE_SYSTEM] =
-            writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
         stateChecksums[STATE_SECURE] =
-            writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
         stateChecksums[STATE_GLOBAL] =
-            writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
         stateChecksums[STATE_LOCALE] =
-            writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
+                writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
         stateChecksums[STATE_WIFI_SUPPLICANT] =
-            writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
-                    wifiSupplicantData, data);
+                writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
+                        wifiSupplicantData, data);
         stateChecksums[STATE_WIFI_CONFIG] =
-            writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
-                    data);
+                writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
+                        data);
         stateChecksums[STATE_LOCK_SETTINGS] =
-            writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
-                    lockSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
+                        lockSettingsData, data);
+        stateChecksums[STATE_SOFTAP_CONFIG] =
+                writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
+                        softApConfigData, data);
+        stateChecksums[STATE_NETWORK_POLICIES] =
+                writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
+                        netPoliciesData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -503,8 +527,8 @@
                     restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
                             restoredSupplicantData, restoredSupplicantData.length);
                     FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
-                            FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                            FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                            FileUtils.S_IRUSR | FileUtils.S_IWUSR
+                            | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
                             Process.myUid(), Process.WIFI_UID);
                 }
                 if (restoredWifiConfigFile != null) {
@@ -516,8 +540,8 @@
                     Settings.Global.putInt(getContentResolver(),
                             Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways);
                 }
-                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
-                        retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
+                        || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
             }
         }
     }
@@ -542,27 +566,55 @@
         while (data.readNextHeader()) {
             final String key = data.getKey();
             final int size = data.getDataSize();
-            if (KEY_SYSTEM.equals(key)) {
-                restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
-                mSettingsHelper.applyAudioSettings();
-            } else if (KEY_SECURE.equals(key)) {
-                restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
-            } else if (KEY_GLOBAL.equals(key)) {
-                restoreSettings(data, Settings.Global.CONTENT_URI, null);
-            } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
-                initWifiRestoreIfNecessary();
-                mWifiRestore.incorporateWifiSupplicant(data);
-            } else if (KEY_LOCALE.equals(key)) {
-                byte[] localeData = new byte[size];
-                data.readEntityData(localeData, 0, size);
-                mSettingsHelper.setLocaleData(localeData, size);
-            } else if (KEY_WIFI_CONFIG.equals(key)) {
-                initWifiRestoreIfNecessary();
-                mWifiRestore.incorporateWifiConfigFile(data);
-            } else if (KEY_LOCK_SETTINGS.equals(key)) {
-                restoreLockSettings(data);
-             } else {
-                data.skipEntityData();
+            switch (key) {
+                case KEY_SYSTEM :
+                    restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
+                    mSettingsHelper.applyAudioSettings();
+                    break;
+
+                case KEY_SECURE :
+                    restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
+                    break;
+
+                case KEY_GLOBAL :
+                    restoreSettings(data, Settings.Global.CONTENT_URI, null);
+                    break;
+
+                case KEY_WIFI_SUPPLICANT :
+                    initWifiRestoreIfNecessary();
+                    mWifiRestore.incorporateWifiSupplicant(data);
+                    break;
+
+                case KEY_LOCALE :
+                    byte[] localeData = new byte[size];
+                    data.readEntityData(localeData, 0, size);
+                    mSettingsHelper.setLocaleData(localeData, size);
+                    break;
+
+                case KEY_WIFI_CONFIG :
+                    initWifiRestoreIfNecessary();
+                    mWifiRestore.incorporateWifiConfigFile(data);
+                    break;
+
+                case KEY_LOCK_SETTINGS :
+                    restoreLockSettings(data);
+                    break;
+
+                case KEY_SOFTAP_CONFIG :
+                    byte[] softapData = new byte[size];
+                    data.readEntityData(softapData, 0, size);
+                    restoreSoftApConfiguration(softapData);
+                    break;
+
+                case KEY_NETWORK_POLICIES:
+                    byte[] netPoliciesData = new byte[size];
+                    data.readEntityData(netPoliciesData, 0, size);
+                    restoreNetworkPolicies(netPoliciesData);
+                    break;
+
+                default :
+                    data.skipEntityData();
+
             }
         }
 
@@ -589,6 +641,8 @@
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
+        byte[] softApConfigData = getSoftAPConfiguration();
+        byte[] netPoliciesData = getNetworkPolicies();
 
         // Write the data to the staging file, then emit that as our tarfile
         // representation of the backed-up settings.
@@ -605,16 +659,22 @@
             if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
             out.writeInt(systemSettingsData.length);
             out.write(systemSettingsData);
-            if (DEBUG_BACKUP) Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
+            if (DEBUG_BACKUP) {
+                Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
+            }
             out.writeInt(secureSettingsData.length);
             out.write(secureSettingsData);
-            if (DEBUG_BACKUP) Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
+            if (DEBUG_BACKUP) {
+                Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
+            }
             out.writeInt(globalSettingsData.length);
             out.write(globalSettingsData);
             if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
             out.writeInt(locale.length);
             out.write(locale);
-            if (DEBUG_BACKUP) Log.d(TAG, wifiSupplicantData.length + " bytes of wifi supplicant data");
+            if (DEBUG_BACKUP) {
+                Log.d(TAG, wifiSupplicantData.length + " bytes of wifi supplicant data");
+            }
             out.writeInt(wifiSupplicantData.length);
             out.write(wifiSupplicantData);
             if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
@@ -623,6 +683,12 @@
             if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
             out.writeInt(lockSettingsData.length);
             out.write(lockSettingsData);
+            if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
+            out.writeInt(softApConfigData.length);
+            out.write(softApConfigData);
+            if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
+            out.writeInt(netPoliciesData.length);
+            out.write(netPoliciesData);
 
             out.flush();    // also flushes downstream
 
@@ -691,12 +757,12 @@
             int retainedWifiState = enableWifi(false);
             restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
             FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
-                    FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                    FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                    FileUtils.S_IRUSR | FileUtils.S_IWUSR
+                    | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
                     Process.myUid(), Process.WIFI_UID);
             // retain the previous WIFI state.
-            enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
-                    retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
+                    || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
 
             // wifi config
             nBytes = in.readInt();
@@ -714,7 +780,26 @@
                     restoreLockSettings(buffer, nBytes);
                 }
             }
-
+            // softap config
+            if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                if (nBytes > 0) {
+                    in.readFully(buffer, 0, nBytes);
+                    restoreSoftApConfiguration(buffer);
+                }
+            }
+            // network policies
+            if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                if (nBytes > 0) {
+                    in.readFully(buffer, 0, nBytes);
+                    restoreNetworkPolicies(buffer);
+                }
+            }
             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
         } else {
             data.close();
@@ -899,7 +984,7 @@
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value);
 
             if (DEBUG) {
-                Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
+                Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
             }
         }
     }
@@ -1031,12 +1116,12 @@
 
             //Will truncate read on a very long file,
             //should not happen for a config file
-            byte[] bytes = new byte[(int)file.length()];
+            byte[] bytes = new byte[(int) file.length()];
 
             int offset = 0;
             int numRead = 0;
             while (offset < bytes.length
-                    && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
+                    && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                 offset += numRead;
             }
 
@@ -1057,7 +1142,6 @@
                 }
             }
         }
-
     }
 
     private void restoreFileData(String filename, byte[] bytes, int size) {
@@ -1160,6 +1244,87 @@
         }
     }
 
+    private byte[] getSoftAPConfiguration() {
+        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        try {
+            return wifiManager.getWifiApConfiguration().getBytesForBackup();
+        } catch (IOException ioe) {
+            Log.e(TAG, "Failed to marshal SoftAPConfiguration" + ioe.getMessage());
+            return new byte[0];
+        }
+    }
+
+    private void restoreSoftApConfiguration(byte[] data) {
+        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        try {
+            WifiConfiguration config = WifiConfiguration
+                    .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
+            if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
+            wifiManager.setWifiApConfiguration(config);
+        } catch (IOException | BackupUtils.BadVersionException e) {
+            Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
+        }
+    }
+
+    private byte[] getNetworkPolicies() {
+        NetworkPolicyManager networkPolicyManager =
+                (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
+        NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if (policies != null && policies.length != 0) {
+            DataOutputStream out = new DataOutputStream(baos);
+            try {
+                out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
+                out.writeInt(policies.length);
+                for (NetworkPolicy policy : policies) {
+                    if (policy != null) {
+                        byte[] marshaledPolicy = policy.getBytesForBackup();
+                        out.writeByte(BackupUtils.NOT_NULL);
+                        out.writeInt(marshaledPolicy.length);
+                        out.write(marshaledPolicy);
+                    } else {
+                        out.writeByte(BackupUtils.NULL);
+                    }
+                }
+            } catch (IOException ioe) {
+                Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
+                baos.reset();
+            }
+        }
+        return baos.toByteArray();
+    }
+
+    private void restoreNetworkPolicies(byte[] data) {
+        NetworkPolicyManager networkPolicyManager =
+                (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
+        if (data != null && data.length != 0) {
+            DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+            try {
+                int version = in.readInt();
+                if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
+                    throw new BackupUtils.BadVersionException(
+                            "Unknown Backup Serialization Version");
+                }
+                int length = in.readInt();
+                NetworkPolicy[] policies = new NetworkPolicy[length];
+                for (int i = 0; i < length; i++) {
+                    byte isNull = in.readByte();
+                    if (isNull == BackupUtils.NULL) continue;
+                    int byteLength = in.readInt();
+                    byte[] policyData = new byte[byteLength];
+                    in.read(policyData, 0, byteLength);
+                    policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
+                            new DataInputStream(new ByteArrayInputStream(policyData)));
+                }
+                // Only set the policies if there was no error in the restore operation
+                networkPolicyManager.setNetworkPolicies(policies);
+            } catch (NullPointerException | IOException | BackupUtils.BadVersionException e) {
+                // NPE can be thrown when trying to instantiate a NetworkPolicy
+                Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
+            }
+        }
+    }
+
     /**
      * Write an int in BigEndian into the byte array.
      * @param out byte array
@@ -1181,11 +1346,10 @@
     }
 
     private int readInt(byte[] in, int pos) {
-        int result =
-                ((in[pos    ] & 0xFF) << 24) |
-                ((in[pos + 1] & 0xFF) << 16) |
-                ((in[pos + 2] & 0xFF) <<  8) |
-                ((in[pos + 3] & 0xFF) <<  0);
+        int result = ((in[pos] & 0xFF) << 24)
+                | ((in[pos + 1] & 0xFF) << 16)
+                | ((in[pos + 2] & 0xFF) <<  8)
+                | ((in[pos + 3] & 0xFF) <<  0);
         return result;
     }
 
@@ -1202,4 +1366,4 @@
         }
         return WifiManager.WIFI_STATE_UNKNOWN;
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5fd39c0..dc62609 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -49,7 +49,9 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Random;
 
@@ -107,27 +109,33 @@
     // so callers can wait for completion.
     private final CountDownLatch mCountDownLatch;
 
-    private class Measurement {
+    public class Measurement {
         private static final String SUCCEEDED = "SUCCEEDED";
         private static final String FAILED = "FAILED";
 
-        // TODO: Refactor to make these private for better encapsulation.
-        public String description = "";
-        public long startTime;
-        public long finishTime;
-        public String result = "";
-        public Thread thread;
+        private boolean succeeded;
 
-        public void recordSuccess(String msg) {
+        // Package private.  TODO: investigate better encapsulation.
+        String description = "";
+        long startTime;
+        long finishTime;
+        String result = "";
+        Thread thread;
+
+        public boolean checkSucceeded() { return succeeded; }
+
+        void recordSuccess(String msg) {
             maybeFixupTimes();
+            succeeded = true;
             result = SUCCEEDED + ": " + msg;
             if (mCountDownLatch != null) {
                 mCountDownLatch.countDown();
             }
         }
 
-        public void recordFailure(String msg) {
+        void recordFailure(String msg) {
             maybeFixupTimes();
+            succeeded = false;
             result = FAILED + ": " + msg;
             if (mCountDownLatch != null) {
                 mCountDownLatch.countDown();
@@ -265,6 +273,51 @@
         } catch (InterruptedException ignored) {}
     }
 
+    public List<Measurement> getMeasurements() {
+        // TODO: Consider moving waitForMeasurements() in here to minimize the
+        // chance of caller errors.
+
+        ArrayList<Measurement> measurements = new ArrayList(totalMeasurementCount());
+
+        // Sort measurements IPv4 first.
+        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet4Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
+                mExplicitSourceIcmpChecks.entrySet()) {
+            if (entry.getKey().first instanceof Inet4Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet4Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+
+        // IPv6 measurements second.
+        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet6Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
+                mExplicitSourceIcmpChecks.entrySet()) {
+            if (entry.getKey().first instanceof Inet6Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
+            if (entry.getKey() instanceof Inet6Address) {
+                measurements.add(entry.getValue());
+            }
+        }
+
+        return measurements;
+    }
+
     public void dump(IndentingPrintWriter pw) {
         pw.println(TAG + ":" + mDescription);
         final long unfinished = mCountDownLatch.getCount();
@@ -276,30 +329,13 @@
         }
 
         pw.increaseIndent();
-        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
-            if (entry.getKey() instanceof Inet4Address) {
-                pw.println(entry.getValue().toString());
-            }
+
+        String prefix;
+        for (Measurement m : getMeasurements()) {
+            prefix = m.checkSucceeded() ? "." : "F";
+            pw.println(prefix + "  " + m.toString());
         }
-        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
-            if (entry.getKey() instanceof Inet6Address) {
-                pw.println(entry.getValue().toString());
-            }
-        }
-        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
-                mExplicitSourceIcmpChecks.entrySet()) {
-            pw.println(entry.getValue().toString());
-        }
-        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
-            if (entry.getKey() instanceof Inet4Address) {
-                pw.println(entry.getValue().toString());
-            }
-        }
-        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
-            if (entry.getKey() instanceof Inet6Address) {
-                pw.println(entry.getValue().toString());
-            }
-        }
+
         pw.decreaseIndent();
     }
 
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 06b6ee7..027c325 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -92,13 +92,18 @@
          */
 
         // Implementations must call IpManager#completedPreDhcpAction().
+        // TODO: Remove this requirement, perhaps via some
+        // registerForPreDhcpAction()-style mechanism.
         public void onPreDhcpAction() {}
         public void onPostDhcpAction() {}
 
-        // TODO: Kill with fire once DHCP and static configuration are moved
-        // out of WifiStateMachine.
-        public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
-        public void onIPv4ProvisioningFailure() {}
+        // This is purely advisory and not an indication of provisioning
+        // success or failure.  This is only here for callers that want to
+        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+        // DHCPv4 or static IPv4 configuration failure or success can be
+        // determined by whether or not the passed-in DhcpResults object is
+        // null or not.
+        public void onNewDhcpResults(DhcpResults dhcpResults) {}
 
         public void onProvisioningSuccess(LinkProperties newLp) {}
         public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,6 +127,7 @@
 
     private final Object mLock = new Object();
     private final State mStoppedState = new StoppedState();
+    private final State mStoppingState = new StoppingState();
     private final State mStartedState = new StartedState();
 
     private final Context mContext;
@@ -179,6 +185,8 @@
         // Super simple StateMachine.
         addState(mStoppedState);
         addState(mStartedState);
+        addState(mStoppingState);
+
         setInitialState(mStoppedState);
         setLogRecSize(MAX_LOG_RECORDS);
         super.start();
@@ -203,13 +211,11 @@
 
     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
         getInterfaceIndex();
-
         sendMessage(CMD_START, staticIpConfig);
     }
 
     public void startProvisioning() {
         getInterfaceIndex();
-
         sendMessage(CMD_START);
     }
 
@@ -236,6 +242,42 @@
      * Internals.
      */
 
+    @Override
+    protected String getWhatToString(int what) {
+        // TODO: Investigate switching to reflection.
+        switch (what) {
+            case CMD_STOP:
+                return "CMD_STOP";
+            case CMD_START:
+                return "CMD_START";
+            case CMD_CONFIRM:
+                return "CMD_CONFIRM";
+            case EVENT_PRE_DHCP_ACTION_COMPLETE:
+                return "EVENT_PRE_DHCP_ACTION_COMPLETE";
+            case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+                return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
+            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                return "DhcpStateMachine.CMD_PRE_DHCP_ACTION";
+            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                return "DhcpStateMachine.CMD_POST_DHCP_ACTION";
+            case DhcpStateMachine.CMD_ON_QUIT:
+                return "DhcpStateMachine.CMD_ON_QUIT";
+        }
+        return "UNKNOWN:" + Integer.toString(what);
+    }
+
+    @Override
+    protected String getLogRecString(Message msg) {
+        final String logLine = String.format(
+                "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
+                mInterfaceName, mInterfaceIndex,
+                msg.arg1, msg.arg2, Objects.toString(msg.obj));
+        if (VDBG) {
+            Log.d(TAG, getWhatToString(msg.what) + " " + logLine);
+        }
+        return logLine;
+    }
+
     private void getInterfaceIndex() {
         try {
             mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
@@ -260,16 +302,93 @@
         }
     }
 
+    // For now: use WifiStateMachine's historical notion of provisioned.
+    private static boolean isProvisioned(LinkProperties lp) {
+        // For historical reasons, we should connect even if all we have is
+        // an IPv4 address and nothing else.
+        return lp.isProvisioned() || lp.hasIPv4Address();
+    }
+
+    // TODO: Investigate folding all this into the existing static function
+    // LinkProperties.compareProvisioning() or some other single function that
+    // takes two LinkProperties objects and returns a ProvisioningChange
+    // object that is a correct and complete assessment of what changed, taking
+    // account of the asymmetries described in the comments in this function.
+    // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+    private static ProvisioningChange compareProvisioning(
+            LinkProperties oldLp, LinkProperties newLp) {
+        ProvisioningChange delta;
+
+        final boolean wasProvisioned = isProvisioned(oldLp);
+        final boolean isProvisioned = isProvisioned(newLp);
+
+        if (!wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.GAINED_PROVISIONING;
+        } else if (wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.STILL_PROVISIONED;
+        } else if (!wasProvisioned && !isProvisioned) {
+            delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+        } else {
+            // (wasProvisioned && !isProvisioned)
+            //
+            // Note that this is true even if we lose a configuration element
+            // (e.g., a default gateway) that would not be required to advance
+            // into provisioned state. This is intended: if we have a default
+            // router and we lose it, that's a sure sign of a problem, but if
+            // we connect to a network with no IPv4 DNS servers, we consider
+            // that to be a network without DNS servers and connect anyway.
+            //
+            // See the comment below.
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        // Additionally:
+        //
+        // Partial configurations (e.g., only an IPv4 address with no DNS
+        // servers and no default route) are accepted as long as DHCPv4
+        // succeeds. On such a network, isProvisioned() will always return
+        // false, because the configuration is not complete, but we want to
+        // connect anyway. It might be a disconnected network such as a
+        // Chromecast or a wireless printer, for example.
+        //
+        // Because on such a network isProvisioned() will always return false,
+        // delta will never be LOST_PROVISIONING. So check for loss of
+        // provisioning here too.
+        if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+                (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        return delta;
+    }
+
+    private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+        switch (delta) {
+            case GAINED_PROVISIONING:
+                if (VDBG) { Log.d(TAG, "onProvisioningSuccess()"); }
+                mCallback.onProvisioningSuccess(newLp);
+                break;
+
+            case LOST_PROVISIONING:
+                if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); }
+                mCallback.onProvisioningFailure(newLp);
+                break;
+
+            default:
+                if (VDBG) { Log.d(TAG, "onLinkPropertiesChange()"); }
+                mCallback.onLinkPropertiesChange(newLp);
+                break;
+        }
+    }
+
     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
         if (mIpReachabilityMonitor != null) {
             mIpReachabilityMonitor.updateLinkProperties(newLp);
         }
 
-        // TODO: Figure out whether and how to incorporate static configuration
-        // into the notion of provisioning.
         ProvisioningChange delta;
         synchronized (mLock) {
-            delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
+            delta = compareProvisioning(mLinkProperties, newLp);
             mLinkProperties = new LinkProperties(newLp);
         }
 
@@ -351,15 +470,45 @@
 
     private void handleIPv4Success(DhcpResults dhcpResults) {
         mDhcpResults = new DhcpResults(dhcpResults);
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningSuccess(dhcpResults);
+        final LinkProperties newLp = assembleLinkProperties();
+        final ProvisioningChange delta = setLinkProperties(newLp);
+
+        if (VDBG) {
+            Log.d(TAG, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+        }
+        mCallback.onNewDhcpResults(dhcpResults);
+
+        dispatchCallback(delta, newLp);
     }
 
     private void handleIPv4Failure() {
+        // TODO: Figure out to de-dup this and the same code in DhcpClient.
         clearIPv4Address();
         mDhcpResults = null;
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningFailure();
+        final LinkProperties newLp = assembleLinkProperties();
+        ProvisioningChange delta = setLinkProperties(newLp);
+        // If we've gotten here and we're still not provisioned treat that as
+        // a total loss of provisioning.
+        //
+        // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+        // there was no usable IPv6 obtained before the DHCPv4 timeout.
+        //
+        // Regardless: GAME OVER.
+        //
+        // TODO: Make the DHCP client not time out and just continue in
+        // exponential backoff. Callers such as Wi-Fi which need a timeout
+        // should implement it themselves.
+        if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        if (VDBG) { Log.d(TAG, "onNewDhcpResults(null)"); }
+        mCallback.onNewDhcpResults(null);
+
+        dispatchCallback(delta, newLp);
+        if (delta == ProvisioningChange.LOST_PROVISIONING) {
+            transitionTo(mStoppingState);
+        }
     }
 
     class StoppedState extends State {
@@ -391,13 +540,8 @@
                     break;
 
                 case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Shutting down DHCPv4 progresses simultaneously with
-                    // transitioning to StoppedState, so we can receive this
-                    // message after we've already transitioned here.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                    // Everything is already stopped.
+                    Log.e(TAG, "Unexpected CMD_ON_QUIT (already stopped).");
                     break;
 
                 default:
@@ -407,6 +551,30 @@
         }
     }
 
+    class StoppingState extends State {
+        @Override
+        public void enter() {
+            if (mDhcpStateMachine == null) {
+                // There's no DHCPv4 for which to wait; proceed to stopped.
+                transitionTo(mStoppedState);
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case DhcpStateMachine.CMD_ON_QUIT:
+                    mDhcpStateMachine = null;
+                    transitionTo(mStoppedState);
+                    break;
+
+                default:
+                    deferMessage(msg);
+            }
+            return HANDLED;
+        }
+    }
+
     class StartedState extends State {
         @Override
         public void enter() {
@@ -439,7 +607,9 @@
                 if (applyStaticIpConfig()) {
                     handleIPv4Success(new DhcpResults(mStaticIpConfig));
                 } else {
-                    handleIPv4Failure();
+                    if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); }
+                    mCallback.onProvisioningFailure(getLinkProperties());
+                    transitionTo(mStoppingState);
                 }
             } else {
                 // Start DHCPv4.
@@ -457,7 +627,6 @@
             if (mDhcpStateMachine != null) {
                 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
                 mDhcpStateMachine.doQuit();
-                mDhcpStateMachine = null;
             }
 
             resetLinkProperties();
@@ -500,28 +669,15 @@
                         break;
                     }
                     final ProvisioningChange delta = setLinkProperties(newLp);
-
-                    // NOTE: The only receiver of these callbacks currently
-                    // treats all three of them identically, namely it calls
-                    // IpManager#getLinkProperties() and makes its own determination.
-                    switch (delta) {
-                        case GAINED_PROVISIONING:
-                            mCallback.onProvisioningSuccess(newLp);
-                            break;
-
-                        case LOST_PROVISIONING:
-                            mCallback.onProvisioningFailure(newLp);
-                            break;
-
-                        default:
-                            // TODO: Only notify on STILL_PROVISIONED?
-                            mCallback.onLinkPropertiesChange(newLp);
-                            break;
+                    dispatchCallback(delta, newLp);
+                    if (delta == ProvisioningChange.LOST_PROVISIONING) {
+                        transitionTo(mStoppedState);
                     }
                     break;
                 }
 
                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                    if (VDBG) { Log.d(TAG, "onPreDhcpAction()"); }
                     mCallback.onPreDhcpAction();
                     break;
 
@@ -529,6 +685,7 @@
                     // Note that onPostDhcpAction() is likely to be
                     // asynchronous, and thus there is no guarantee that we
                     // will be able to observe any of its effects here.
+                    if (VDBG) { Log.d(TAG, "onPostDhcpAction()"); }
                     mCallback.onPostDhcpAction();
 
                     final DhcpResults dhcpResults = (DhcpResults) msg.obj;
@@ -546,11 +703,9 @@
                 }
 
                 case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Regardless, we ignore it.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                    // DHCPv4 quit early for some reason.
+                    Log.e(TAG, "Unexpected CMD_ON_QUIT.");
+                    mDhcpStateMachine = null;
                     break;
 
                 default:
diff --git a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
index 611ed15..9fc1706 100644
--- a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
+++ b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
@@ -36,15 +36,15 @@
         mMoTree = moTree;
     }
 
-    public String getmBaseUri() {
+    public String getBaseUri() {
         return mBaseUri;
     }
 
-    public String getmUrn() {
+    public String getUrn() {
         return mUrn;
     }
 
-    public String getmMoTree() {
+    public String getMoTree() {
         return mMoTree;
     }
 
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 503e4a2..9137d9d 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -136,6 +136,9 @@
     public static final int REASON_INVALID_REQUEST          = -4;
     /** Do not have required permission */
     public static final int REASON_PERMISSION_DENIED        = -5;
+    /** Ranging failed because responder role is enabled in STA mode.*/
+    public static final int
+            REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON  = -6;
 
     public static final String DESCRIPTION_KEY  = "android.net.wifi.RttManager.Description";
 
@@ -191,6 +194,8 @@
         public int preambleSupported;
         //RTT bandwidth supported
         public int bwSupported;
+        // Whether STA responder role is supported.
+        public boolean responderSupported;
 
         @Override
         public String toString() {
@@ -244,6 +249,9 @@
 
             sb.append("is supported.");
 
+            sb.append(" STA responder role is ")
+                .append(responderSupported ? "supported" : "not supported.");
+
             return sb.toString();
         }
         /** Implement the Parcelable interface {@hide} */
@@ -261,7 +269,7 @@
             dest.writeInt(lcrSupported ? 1 : 0);
             dest.writeInt(preambleSupported);
             dest.writeInt(bwSupported);
-
+            dest.writeInt(responderSupported ? 1 : 0);
         }
 
         /** Implement the Parcelable interface {@hide} */
@@ -275,6 +283,7 @@
                         capabilities.lcrSupported = in.readInt() == 1 ? true : false;
                         capabilities.preambleSupported = in.readInt();
                         capabilities.bwSupported = in.readInt();
+                        capabilities.responderSupported = (in.readInt() == 1);
                         return capabilities;
                     }
                 /** Implement the Parcelable interface {@hide} */
@@ -898,6 +907,160 @@
         sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
     }
 
+    /**
+     * Callbacks for responder operations.
+     * <p>
+     * A {@link ResponderCallback} is the handle to the calling client. {@link RttManager} will keep
+     * a reference to the callback for the entire period when responder is enabled. The same
+     * callback as used in enabling responder needs to be passed for disabling responder.
+     * The client can freely destroy or reuse the callback after {@link RttManager#disableResponder}
+     * is called.
+     */
+    public abstract static class ResponderCallback {
+        /** Callback when responder is enabled. */
+        public abstract void onResponderEnabled(ResponderConfig config);
+        /** Callback when enabling responder failed. */
+        public abstract void onResponderEnableFailure(int reason);
+        // TODO: consider adding onResponderAborted once it's supported.
+    }
+
+    /**
+     * Enable Wi-Fi RTT responder mode on the device. The enabling result will be delivered via
+     * {@code callback}.
+     * <p>
+     * Note calling this method with the same callback when the responder is already enabled won't
+     * change the responder state, a cached {@link ResponderConfig} from the last enabling will be
+     * returned through the callback.
+     *
+     * @param callback Callback for responder enabling/disabling result.
+     * @throws IllegalArgumentException If {@code callback} is null.
+     */
+    public void enableResponder(ResponderCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        validateChannel();
+        int key = putListenerIfAbsent(callback);
+        sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+    }
+
+    /**
+     * Disable Wi-Fi RTT responder mode on the device. The {@code callback} needs to be the
+     * same one used in {@link #enableResponder(ResponderCallback)}.
+     * <p>
+     * Calling this method when responder isn't enabled won't have any effect. The callback can be
+     * reused for enabling responder after this method is called.
+     *
+     * @param callback The same callback used for enabling responder.
+     * @throws IllegalArgumentException If {@code callback} is null.
+     */
+    public void disableResponder(ResponderCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        validateChannel();
+        int key = removeListener(callback);
+        if (key == INVALID_KEY) {
+            Log.e(TAG, "responder not enabled yet");
+            return;
+        }
+        sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+    }
+
+    /**
+     * Configuration used for RTT responder mode. The configuration information can be used by a
+     * peer device to range the responder.
+     *
+     * @see ScanResult
+     */
+    public static class ResponderConfig implements Parcelable {
+
+        // TODO: make all fields final once we can get mac address from responder HAL APIs.
+        /**
+         * Wi-Fi mac address used for responder mode.
+         */
+        public String macAddress = "";
+
+        /**
+         * The primary 20 MHz frequency (in MHz) of the channel where responder is enabled.
+         * @see ScanResult#frequency
+         */
+        public int frequency;
+
+        /**
+         * Center frequency of the channel where responder is enabled on. Only in use when channel
+         * width is at least 40MHz.
+         * @see ScanResult#centerFreq0
+         */
+        public int centerFreq0;
+
+        /**
+         * Center frequency of the second segment when channel width is 80 + 80 MHz.
+         * @see ScanResult#centerFreq1
+         */
+        public int centerFreq1;
+
+        /**
+         * Width of the channel where responder is enabled on.
+         * @see ScanResult#channelWidth
+         */
+        public int channelWidth;
+
+        /**
+         * Preamble supported by responder.
+         */
+        public int preamble;
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("macAddress = ").append(macAddress)
+                    .append(" frequency = ").append(frequency)
+                    .append(" centerFreq0 = ").append(centerFreq0)
+                    .append(" centerFreq1 = ").append(centerFreq1)
+                    .append(" channelWidth = ").append(channelWidth)
+                    .append(" preamble = ").append(preamble);
+            return builder.toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(macAddress);
+            dest.writeInt(frequency);
+            dest.writeInt(centerFreq0);
+            dest.writeInt(centerFreq1);
+            dest.writeInt(channelWidth);
+            dest.writeInt(preamble);
+        }
+
+        /** Implement {@link Parcelable} interface */
+        public static final Parcelable.Creator<ResponderConfig> CREATOR =
+                new Parcelable.Creator<ResponderConfig>() {
+            @Override
+            public ResponderConfig createFromParcel(Parcel in) {
+                ResponderConfig config = new ResponderConfig();
+                config.macAddress = in.readString();
+                config.frequency = in.readInt();
+                config.centerFreq0 = in.readInt();
+                config.centerFreq1 = in.readInt();
+                config.channelWidth = in.readInt();
+                config.preamble = in.readInt();
+                return config;
+            }
+
+            @Override
+            public ResponderConfig[] newArray(int size) {
+                return new ResponderConfig[size];
+            }
+        };
+
+    }
+
     /* private methods */
     public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER;
 
@@ -906,6 +1069,12 @@
     public static final int CMD_OP_FAILED               = BASE + 2;
     public static final int CMD_OP_SUCCEEDED            = BASE + 3;
     public static final int CMD_OP_ABORTED              = BASE + 4;
+    public static final int CMD_OP_ENABLE_RESPONDER     = BASE + 5;
+    public static final int CMD_OP_DISABLE_RESPONDER    = BASE + 6;
+    public static final int
+            CMD_OP_ENALBE_RESPONDER_SUCCEEDED           = BASE + 7;
+    public static final int
+            CMD_OP_ENALBE_RESPONDER_FAILED              = BASE + 8;
 
     private Context mContext;
     private IRttManager mService;
@@ -992,6 +1161,23 @@
         return key;
     }
 
+    // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener.
+    private static int putListenerIfAbsent(Object listener) {
+        if (listener == null) return INVALID_KEY;
+        synchronized (sListenerMapLock) {
+            int key = getListenerKey(listener);
+            if (key != INVALID_KEY) {
+                return key;
+            }
+            do {
+                key = sListenerKey++;
+            } while (key == INVALID_KEY);
+            sListenerMap.put(key, listener);
+            return key;
+        }
+
+    }
+
     private static Object getListener(int key) {
         if (key == INVALID_KEY) return null;
         synchronized (sListenerMapLock) {
@@ -1047,9 +1233,9 @@
                         // to fail and throw an exception
                         sAsyncChannel = null;
                     }
-                    sConnected.countDown();
                     return;
                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+                    sConnected.countDown();
                     return;
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                     Log.e(TAG, "Channel connection lost");
@@ -1082,6 +1268,14 @@
                     ((RttListener) listener).onAborted();
                     removeListener(msg.arg2);
                     break;
+                case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
+                    ResponderConfig config = (ResponderConfig) msg.obj;
+                    ((ResponderCallback) (listener)).onResponderEnabled(config);
+                    break;
+                case CMD_OP_ENALBE_RESPONDER_FAILED:
+                    ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
+                    removeListener(msg.arg2);
+                    break;
                 default:
                     if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
                     return;
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 4a86c59..31da670 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -332,6 +332,7 @@
     public static class InformationElement {
         public static final int EID_SSID = 0;
         public static final int EID_BSS_LOAD = 11;
+        public static final int EID_RSN = 48;
         public static final int EID_HT_OPERATION = 61;
         public static final int EID_INTERWORKING = 107;
         public static final int EID_ROAMING_CONSORTIUM = 111;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c8065f9..5d94e53 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -26,7 +26,12 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.BackupUtils;
 
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.HashMap;
@@ -37,6 +42,10 @@
  */
 public class WifiConfiguration implements Parcelable {
     private static final String TAG = "WifiConfiguration";
+    /**
+     * Current Version of the Backup Serializer.
+    */
+    private static final int BACKUP_VERSION = 2;
     /** {@hide} */
     public static final String ssidVarName = "ssid";
     /** {@hide} */
@@ -1449,10 +1458,10 @@
             if (diff <= 0) {
                 sbuf.append(" blackListed since <incorrect>");
             } else {
-                sbuf.append(" blackListed: ").append(Long.toString(diff/1000)).append( "sec ");
+                sbuf.append(" blackListed: ").append(Long.toString(diff / 1000)).append("sec ");
             }
         }
-        if (creatorUid != 0)  sbuf.append(" cuid=" + Integer.toString(creatorUid));
+        if (creatorUid != 0) sbuf.append(" cuid=" + creatorUid);
         if (creatorName != null) sbuf.append(" cname=" + creatorName);
         if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
         if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
@@ -1467,7 +1476,7 @@
             if (diff <= 0) {
                 sbuf.append("lastConnected since <incorrect>");
             } else {
-                sbuf.append("lastConnected: ").append(Long.toString(diff/1000)).append( "sec ");
+                sbuf.append("lastConnected: ").append(Long.toString(diff / 1000)).append("sec ");
             }
         }
         if (this.lastConnectionFailure != 0) {
@@ -1476,8 +1485,8 @@
             if (diff <= 0) {
                 sbuf.append("lastConnectionFailure since <incorrect> ");
             } else {
-                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff/1000));
-                sbuf.append( "sec ");
+                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff / 1000));
+                sbuf.append("sec ");
             }
         }
         if (this.lastRoamingFailure != 0) {
@@ -1486,20 +1495,19 @@
             if (diff <= 0) {
                 sbuf.append("lastRoamingFailure since <incorrect> ");
             } else {
-                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff/1000));
-                sbuf.append( "sec ");
+                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff / 1000));
+                sbuf.append("sec ");
             }
         }
         sbuf.append("roamingFailureBlackListTimeMilli: ").
                 append(Long.toString(this.roamingFailureBlackListTimeMilli));
         sbuf.append('\n');
         if (this.linkedConfigurations != null) {
-            for(String key : this.linkedConfigurations.keySet()) {
+            for (String key : this.linkedConfigurations.keySet()) {
                 sbuf.append(" linked: ").append(key);
                 sbuf.append('\n');
             }
         }
-
         sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
         sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
         sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
@@ -1989,4 +1997,43 @@
                 return new WifiConfiguration[size];
             }
         };
-}
+
+    /**
+     * Serializes the object for backup
+     * @hide
+     */
+    public byte[] getBytesForBackup() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(baos);
+
+        out.writeInt(BACKUP_VERSION);
+        BackupUtils.writeString(out, SSID);
+        out.writeInt(apBand);
+        out.writeInt(apChannel);
+        BackupUtils.writeString(out, preSharedKey);
+        out.writeInt(getAuthType());
+        return baos.toByteArray();
+    }
+
+    /**
+     * Deserializes a byte array into the WiFiConfiguration Object
+     * @hide
+     */
+    public static WifiConfiguration getWifiConfigFromBackup(DataInputStream in) throws IOException,
+            BackupUtils.BadVersionException {
+        WifiConfiguration config = new WifiConfiguration();
+        int version = in.readInt();
+        if (version < 1 || version > BACKUP_VERSION) {
+            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
+        }
+
+        if (version == 1) return null; // Version 1 is a bad dataset.
+
+        config.SSID = BackupUtils.readString(in);
+        config.apBand = in.readInt();
+        config.apChannel = in.readInt();
+        config.preSharedKey = BackupUtils.readString(in);
+        config.allowedKeyManagement.set(in.readInt());
+        return config;
+    }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index d77f2bb..a406fd7 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -101,6 +101,8 @@
     /** @hide */
     public static final String CA_CERT_KEY         = "ca_cert";
     /** @hide */
+    public static final String CA_PATH_KEY         = "ca_path";
+    /** @hide */
     public static final String ENGINE_KEY          = "engine";
     /** @hide */
     public static final String ENGINE_ID_KEY       = "engine_id";
@@ -656,6 +658,33 @@
         mCaCerts = null;
     }
 
+    /**
+     * Set the ca_path directive on wpa_supplicant.
+     *
+     * From wpa_supplicant documentation:
+     *
+     * Directory path for CA certificate files (PEM). This path may contain
+     * multiple CA certificates in OpenSSL format. Common use for this is to
+     * point to system trusted CA list which is often installed into directory
+     * like /etc/ssl/certs. If configured, these certificates are added to the
+     * list of trusted CAs. ca_cert may also be included in that case, but it is
+     * not required.
+     * @param domain The path for CA certificate files
+     * @hide
+     */
+    public void setCaPath(String path) {
+        setFieldValue(CA_PATH_KEY, path);
+    }
+
+    /**
+     * Get the domain_suffix_match value. See setDomSuffixMatch.
+     * @return The path for CA certificate files.
+     * @hide
+     */
+    public String getCaPath() {
+        return getFieldValue(CA_PATH_KEY, "");
+    }
+
     /** Set Client certificate alias.
      *
      * <p> See the {@link android.security.KeyChain} for details on installing or choosing