DO NOT MERGE Manually merge commit 'f28778064383821ccf48c9570191f34a24aa513f' into nyc-mr1-dev-plus-aosp

Change-Id: I7cb1f97a5b931c2057a811d7947f47ad7320ef1c
diff --git a/Android.mk b/Android.mk
index 3da1a83..b644a17 100644
--- a/Android.mk
+++ b/Android.mk
@@ -452,9 +452,9 @@
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
-	wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \
+	wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl \
 	wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
-	wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \
+	wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
 	wifi/java/android/net/wifi/IWifiScanner.aidl \
 	wifi/java/android/net/wifi/IRttManager.aidl \
@@ -546,10 +546,8 @@
 	frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
 	frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
 	frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/PublishData.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeData.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/PublishSettings.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/PublishConfig.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
index 94214ff..633dd97 100644
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
@@ -52,7 +52,7 @@
                 IWifiManager wifiMgr
                         = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
                 try {
-                    wifiMgr.setWifiEnabled(flag);
+                    wifiMgr.setWifiEnabled("com.android.shell", flag);
                 }
                 catch (RemoteException e) {
                     System.err.println("Wi-Fi operation failed: " + e);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 55744b9..42ddf10 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -515,15 +515,15 @@
             }});
 
         registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class,
-                new StaticServiceFetcher<WifiNanManager>() {
+                new CachedServiceFetcher<WifiNanManager>() {
             @Override
-            public WifiNanManager createService() {
+            public WifiNanManager createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE);
                 IWifiNanManager service = IWifiNanManager.Stub.asInterface(b);
                 if (service == null) {
                     return null;
                 }
-                return new WifiNanManager(service);
+                return new WifiNanManager(ctx.getOuterContext(), service);
             }});
 
         registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a299a40..978d043 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3137,7 +3137,7 @@
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.net.wifi.nan.WifiNanManager} for handling management of
-     * Wi-Fi NAN discovery and connections.
+     * Wi-Fi NAN.
      *
      * @see #getSystemService
      * @see android.net.wifi.nan.WifiNanManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 281d6f6..9660a57 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2040,8 +2040,7 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device supports Wi-Fi Aware (NAN)
-     * networking.
+     * {@link #hasSystemFeature}: The device supports Wi-Fi NAN.
      *
      * @hide PROPOSED_NAN_API
      */
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 0b548d5..1c3d6bd 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -24,7 +24,6 @@
 import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.UidRange;
-import android.net.wifi.WifiConfiguration;
 import android.os.INetworkActivityListener;
 
 /**
@@ -223,27 +222,6 @@
     void detachPppd(String tty);
 
     /**
-     * Load firmware for operation in the given mode. Currently the three
-     * modes supported are "AP", "STA" and "P2P".
-     */
-    void wifiFirmwareReload(String wlanIface, String mode);
-
-    /**
-     * Start Wifi Access Point
-     */
-    void startAccessPoint(in WifiConfiguration wifiConfig, String iface);
-
-    /**
-     * Stop Wifi Access Point
-     */
-    void stopAccessPoint(String iface);
-
-    /**
-     * Set Access Point config
-     */
-    void setAccessPoint(in WifiConfiguration wifiConfig, String iface);
-
-    /**
      ** DATA USAGE RELATED
      **/
 
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index a5a3dba..b0d45e1d1 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -750,6 +750,14 @@
         /** The destination state when transitionTo has been invoked */
         private State mDestState;
 
+        /**
+         * Indicates if a transition is in progress
+         *
+         * This will be true for all calls of State.exit and all calls of State.enter except for the
+         * last enter call for the current destination state.
+         */
+        private boolean mTransitionInProgress = false;
+
         /** The list of deferred messages */
         private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
 
@@ -862,6 +870,8 @@
                      * invoke the exit methods then the enter methods.
                      */
                     StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
+                    // flag is cleared in invokeEnterMethods before entering the target state
+                    mTransitionInProgress = true;
                     invokeExitMethods(commonStateInfo);
                     int stateStackEnteringIndex = moveTempStateStackToStateStack();
                     invokeEnterMethods(stateStackEnteringIndex);
@@ -1017,10 +1027,15 @@
          */
         private final void invokeEnterMethods(int stateStackEnteringIndex) {
             for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
+                if (stateStackEnteringIndex == mStateStackTopIndex) {
+                    // Last enter state for transition
+                    mTransitionInProgress = false;
+                }
                 if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
                 mStateStack[i].state.enter();
                 mStateStack[i].active = true;
             }
+            mTransitionInProgress = false; // ensure flag set to false if no methods called
         }
 
         /**
@@ -1196,6 +1211,10 @@
 
         /** @see StateMachine#transitionTo(IState) */
         private final void transitionTo(IState destState) {
+            if (mTransitionInProgress) {
+                Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
+                        mDestState + ", new target state=" + destState);
+            }
             mDestState = (State) destState;
             if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
         }
@@ -1305,7 +1324,7 @@
      * @param state the state to add
      * @param parent the parent of state
      */
-    protected final void addState(State state, State parent) {
+    public final void addState(State state, State parent) {
         mSmHandler.addState(state, parent);
     }
 
@@ -1313,7 +1332,7 @@
      * Add a new state to the state machine, parent will be null
      * @param state to add
      */
-    protected final void addState(State state) {
+    public final void addState(State state) {
         mSmHandler.addState(state, null);
     }
 
@@ -1323,14 +1342,14 @@
      *
      * @param initialState is the state which will receive the first message.
      */
-    protected final void setInitialState(State initialState) {
+    public final void setInitialState(State initialState) {
         mSmHandler.setInitialState(initialState);
     }
 
     /**
      * @return current message
      */
-    protected final Message getCurrentMessage() {
+    public final Message getCurrentMessage() {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
         if (smh == null) return null;
@@ -1340,7 +1359,7 @@
     /**
      * @return current state
      */
-    protected final IState getCurrentState() {
+    public final IState getCurrentState() {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
         if (smh == null) return null;
@@ -1361,7 +1380,7 @@
      *
      * @param destState will be the state that receives the next message.
      */
-    protected final void transitionTo(IState destState) {
+    public final void transitionTo(IState destState) {
         mSmHandler.transitionTo(destState);
     }
 
@@ -1372,7 +1391,7 @@
      * for all subsequent messages haltedProcessMessage
      * will be called.
      */
-    protected final void transitionToHaltingState() {
+    public final void transitionToHaltingState() {
         mSmHandler.transitionTo(mSmHandler.mHaltingState);
     }
 
@@ -1385,7 +1404,7 @@
      *
      * @param msg is deferred until the next transition.
      */
-    protected final void deferMessage(Message msg) {
+    public final void deferMessage(Message msg) {
         mSmHandler.deferMessage(msg);
     }
 
@@ -1496,7 +1515,7 @@
      *
      * @param string
      */
-    protected void addLogRec(String string) {
+    public void addLogRec(String string) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
         if (smh == null) return;
@@ -1947,7 +1966,7 @@
     /**
      * Quit the state machine after all currently queued up messages are processed.
      */
-    protected final void quit() {
+    public final void quit() {
         // mSmHandler can be null if the state machine is already stopped.
         SmHandler smh = mSmHandler;
         if (smh == null) return;
@@ -1958,7 +1977,7 @@
     /**
      * Quit the state machine immediately all currently queued messages will be discarded.
      */
-    protected final void quitNow() {
+    public final void quitNow() {
         // mSmHandler can be null if the state machine is already stopped.
         SmHandler smh = mSmHandler;
         if (smh == null) return;
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 992cb4e..83d5ce3 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -392,10 +392,10 @@
         StringBuilder sb = new StringBuilder(val.length*2);
         for (int i=0; i<N; i++) {
             int b = val[i];
-            int h = b>>4;
-            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
-            h = b&0xff;
-            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
+            int h = (b >> 4) & 0x0f;
+            sb.append((char)(h >= 10 ? ('a'+h-10) : ('0'+h)));
+            h = b & 0x0f;
+            sb.append((char)(h >= 10 ? ('a'+h-10) : ('0'+h)));
         }
 
         out.text(sb.toString());
@@ -996,6 +996,73 @@
     }
 
     /**
+     * Read a byte[] object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeByteArrayXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the list.
+     *
+     * @param parser The XmlPullParser from which to read the list data.
+     * @param endTag Name of the tag that will end the list, usually "list".
+     * @param name An array of one string, used to return the name attribute
+     *             of the list's tag.
+     *
+     * @return Returns a newly generated byte[].
+     *
+     * @see #writeByteArrayXml
+     */
+    public static final byte[] readThisByteArrayXml(XmlPullParser parser,
+            String endTag, String[] name)
+            throws XmlPullParserException, java.io.IOException {
+
+        int num;
+        try {
+            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+        } catch (NullPointerException e) {
+            throw new XmlPullParserException(
+                    "Need num attribute in byte-array");
+        } catch (NumberFormatException e) {
+            throw new XmlPullParserException(
+                    "Not a number in num attribute in byte-array");
+        }
+
+        byte[] array = new byte[num];
+
+        int eventType = parser.getEventType();
+        do {
+            if (eventType == parser.TEXT) {
+                if (num > 0) {
+                    String values = parser.getText();
+                    if (values == null || values.length() != num * 2) {
+                        throw new XmlPullParserException(
+                                "Invalid value found in byte-array: " + values);
+                    }
+                    // This is ugly, but keeping it to mirror the logic in #writeByteArrayXml.
+                    for (int i = 0; i < num; i ++) {
+                        char nibbleHighChar = values.charAt(2 * i);
+                        char nibbleLowChar = values.charAt(2 * i + 1);
+                        int nibbleHigh = nibbleHighChar > 'a' ? (nibbleHighChar - 'a' + 10)
+                                : (nibbleHighChar - '0');
+                        int nibbleLow = nibbleLowChar > 'a' ? (nibbleLowChar - 'a' + 10)
+                                : (nibbleLowChar - '0');
+                        array[i] = (byte) ((nibbleHigh & 0x0F) << 4 | (nibbleLow & 0x0F));
+                    }
+                }
+            } else if (eventType == parser.END_TAG) {
+                if (parser.getName().equals(endTag)) {
+                    return array;
+                } else {
+                    throw new XmlPullParserException(
+                            "Expected " + endTag + " end tag at: "
+                                    + parser.getName());
+                }
+            }
+            eventType = parser.next();
+        } while (eventType != parser.END_DOCUMENT);
+
+        throw new XmlPullParserException(
+                "Document ended before " + endTag + " end tag");
+    }
+
+    /**
      * Read an int[] object from an XmlPullParser.  The XML data could
      * previously have been generated by writeIntArrayXml().  The XmlPullParser
      * must be positioned <em>after</em> the tag that begins the list.
@@ -1018,10 +1085,10 @@
             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
         } catch (NullPointerException e) {
             throw new XmlPullParserException(
-                    "Need num attribute in byte-array");
+                    "Need num attribute in int-array");
         } catch (NumberFormatException e) {
             throw new XmlPullParserException(
-                    "Not a number in num attribute in byte-array");
+                    "Not a number in num attribute in int-array");
         }
         parser.next();
 
@@ -1377,6 +1444,11 @@
                 "Unexpected end of document in <string>");
         } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
             // all work already done by readThisPrimitiveValueXml
+        } else if (tagName.equals("byte-array")) {
+            res = readThisByteArrayXml(parser, "byte-array", name);
+            name[0] = valueName;
+            //System.out.println("Returning value for " + valueName + ": " + res);
+            return res;
         } else if (tagName.equals("int-array")) {
             res = readThisIntArrayXml(parser, "int-array", name);
             name[0] = valueName;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 382c6c4..e17a80d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -296,6 +296,7 @@
     <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.nan.STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5b017d5..76d7d2b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2582,6 +2582,13 @@
     <string-array translatable="false" name="config_defaultFirstUserRestrictions">
     </string-array>
 
+    <!-- Specifies whether the permissions needed by a legacy app should be
+         reviewed before any of its components can run. A legacy app is one
+         with targetSdkVersion < 23, i.e apps using the old permission model.
+         If review is not required, permissions are reviewed before the app
+         is installed. -->
+    <bool name="config_permissionReviewRequired">false</bool>
+
     <!-- Default value for android:focusableInTouchMode for some framework scrolling containers.
          ListView/GridView are notably absent since this is their default anyway.
          Set to true for watch devices. -->
@@ -2598,4 +2605,5 @@
         <!-- Verizon requires any SMS that starts with //VZWVVM to be treated as a VVM SMS-->
         <item>310004,310010,310012,310013,310590,310890,310910,311110,311270,311271,311272,311273,311274,311275,311276,311277,311278,311279,311280,311281,311282,311283,311284,311285,311286,311287,311288,311289,311390,311480,311481,311482,311483,311484,311485,311486,311487,311488,311489;^//VZWVVM.*</item>
     </string-array>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2d6697e..36afbc6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2702,6 +2702,9 @@
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
 
+  <java-symbol type="bool" name="config_permissionReviewRequired" />
+
+
   <java-symbol type="drawable" name="ic_restart" />
 
 </resources>
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index 302aa87..d29b572 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -39,7 +39,6 @@
 /**
  * Test for StateMachine.
  */
-@Suppress // Failing
 public class StateMachineTest extends TestCase {
     private static final String ENTER = "enter";
     private static final String EXIT = "exit";
@@ -100,7 +99,7 @@
 
         @Override
         public void onQuitting() {
-            log("onQuitting");
+            tlog("onQuitting");
             addLogRec(ON_QUITTING);
             mLogRecs = mThisSm.copyLogRecs();
             synchronized (mThisSm) {
@@ -111,7 +110,7 @@
         class S1 extends State {
             @Override
             public void exit() {
-                log("S1.exit");
+                tlog("S1.exit");
                 addLogRec(EXIT);
             }
             @Override
@@ -119,13 +118,13 @@
                 switch(message.what) {
                     // Sleep and assume the other messages will be queued up.
                     case TEST_CMD_1: {
-                        log("TEST_CMD_1");
+                        tlog("TEST_CMD_1");
                         sleep(500);
                         quit();
                         break;
                     }
                     default: {
-                        log("default what=" + message.what);
+                        tlog("default what=" + message.what);
                         break;
                     }
                 }
@@ -201,7 +200,7 @@
 
         @Override
         public void onQuitting() {
-            log("onQuitting");
+            tlog("onQuitting");
             addLogRec(ON_QUITTING);
             // Get a copy of the log records since we're quitting and they will disappear
             mLogRecs = mThisSm.copyLogRecs();
@@ -214,7 +213,7 @@
         class S1 extends State {
             @Override
             public void exit() {
-                log("S1.exit");
+                tlog("S1.exit");
                 addLogRec(EXIT);
             }
             @Override
@@ -222,13 +221,13 @@
                 switch(message.what) {
                     // Sleep and assume the other messages will be queued up.
                     case TEST_CMD_1: {
-                        log("TEST_CMD_1");
+                        tlog("TEST_CMD_1");
                         sleep(500);
                         quitNow();
                         break;
                     }
                     default: {
-                        log("default what=" + message.what);
+                        tlog("default what=" + message.what);
                         break;
                     }
                 }
@@ -309,12 +308,12 @@
                 // Test transitions in enter on the initial state work
                 addLogRec(ENTER);
                 transitionTo(mS2);
-                log("S1.enter");
+                tlog("S1.enter");
             }
             @Override
             public void exit() {
                 addLogRec(EXIT);
-                log("S1.exit");
+                tlog("S1.exit");
             }
         }
 
@@ -322,7 +321,7 @@
             @Override
             public void enter() {
                 addLogRec(ENTER);
-                log("S2.enter");
+                tlog("S2.enter");
             }
             @Override
             public void exit() {
@@ -332,14 +331,14 @@
                 assertEquals(TEST_CMD_1, getCurrentMessage().what);
                 addLogRec(EXIT);
 
-                log("S2.exit");
+                tlog("S2.exit");
             }
             @Override
             public boolean processMessage(Message message) {
                 // Start a transition to S3 but it will be
                 // changed to a transition to S4 in exit
                 transitionTo(mS3);
-                log("S2.processMessage");
+                tlog("S2.processMessage");
                 return HANDLED;
             }
         }
@@ -348,12 +347,12 @@
             @Override
             public void enter() {
                 addLogRec(ENTER);
-                log("S3.enter");
+                tlog("S3.enter");
             }
             @Override
             public void exit() {
                 addLogRec(EXIT);
-                log("S3.exit");
+                tlog("S3.exit");
             }
         }
 
@@ -363,12 +362,12 @@
                 addLogRec(ENTER);
                 // Test that we can do halting in an enter/exit
                 transitionToHaltingState();
-                log("S4.enter");
+                tlog("S4.enter");
             }
             @Override
             public void exit() {
                 addLogRec(EXIT);
-                log("S4.exit");
+                tlog("S4.exit");
             }
         }
 
@@ -572,7 +571,7 @@
 
             // Set the initial state
             setInitialState(mS1);
-            if (DBG) log("StateMachine1: ctor X");
+            if (DBG) tlog("StateMachine1: ctor X");
         }
 
         class S1 extends State {
@@ -673,7 +672,7 @@
 
             // Set the initial state
             setInitialState(mS1);
-            if (DBG) log("StateMachine2: ctor X");
+            if (DBG) tlog("StateMachine2: ctor X");
         }
 
         class S1 extends State {
@@ -782,7 +781,7 @@
 
             // Set the initial state will be the child
             setInitialState(mChildState);
-            if (DBG) log("StateMachine3: ctor X");
+            if (DBG) tlog("StateMachine3: ctor X");
         }
 
         class ParentState extends State {
@@ -869,7 +868,7 @@
 
             // Set the initial state will be child 1
             setInitialState(mChildState1);
-            if (DBG) log("StateMachine4: ctor X");
+            if (DBG) tlog("StateMachine4: ctor X");
         }
 
         class ParentState extends State {
@@ -969,7 +968,7 @@
 
             // Set the initial state will be the child
             setInitialState(mChildState1);
-            if (DBG) log("StateMachine5: ctor X");
+            if (DBG) tlog("StateMachine5: ctor X");
         }
 
         class ParentState1 extends State {
@@ -1296,7 +1295,7 @@
 
             // Set the initial state
             setInitialState(mS1);
-            if (DBG) log("StateMachine6: ctor X");
+            if (DBG) tlog("StateMachine6: ctor X");
         }
 
         class S1 extends State {
@@ -1383,7 +1382,7 @@
 
             // Set the initial state
             setInitialState(mS1);
-            if (DBG) log("StateMachine7: ctor X");
+            if (DBG) tlog("StateMachine7: ctor X");
         }
 
         class S1 extends State {
@@ -1654,7 +1653,7 @@
 
         Hsm1(String name) {
             super(name);
-            log("ctor E");
+            tlog("ctor E");
 
             // Add states, use indentation to show hierarchy
             addState(mP1);
@@ -1664,22 +1663,22 @@
 
             // Set the initial state
             setInitialState(mS1);
-            log("ctor X");
+            tlog("ctor X");
         }
 
         class P1 extends State {
             @Override
             public void enter() {
-                log("P1.enter");
+                tlog("P1.enter");
             }
             @Override
             public void exit() {
-                log("P1.exit");
+                tlog("P1.exit");
             }
             @Override
             public boolean processMessage(Message message) {
                 boolean retVal;
-                log("P1.processMessage what=" + message.what);
+                tlog("P1.processMessage what=" + message.what);
                 switch(message.what) {
                 case CMD_2:
                     // CMD_2 will arrive in mS2 before CMD_3
@@ -1700,15 +1699,15 @@
         class S1 extends State {
             @Override
             public void enter() {
-                log("S1.enter");
+                tlog("S1.enter");
             }
             @Override
             public void exit() {
-                log("S1.exit");
+                tlog("S1.exit");
             }
             @Override
             public boolean processMessage(Message message) {
-                log("S1.processMessage what=" + message.what);
+                tlog("S1.processMessage what=" + message.what);
                 if (message.what == CMD_1) {
                     // Transition to ourself to show that enter/exit is called
                     transitionTo(mS1);
@@ -1723,16 +1722,16 @@
         class S2 extends State {
             @Override
             public void enter() {
-                log("S2.enter");
+                tlog("S2.enter");
             }
             @Override
             public void exit() {
-                log("S2.exit");
+                tlog("S2.exit");
             }
             @Override
             public boolean processMessage(Message message) {
                 boolean retVal;
-                log("S2.processMessage what=" + message.what);
+                tlog("S2.processMessage what=" + message.what);
                 switch(message.what) {
                 case(CMD_2):
                     sendMessage(CMD_4);
@@ -1754,16 +1753,16 @@
         class P2 extends State {
             @Override
             public void enter() {
-                log("P2.enter");
+                tlog("P2.enter");
                 sendMessage(CMD_5);
             }
             @Override
             public void exit() {
-                log("P2.exit");
+                tlog("P2.exit");
             }
             @Override
             public boolean processMessage(Message message) {
-                log("P2.processMessage what=" + message.what);
+                tlog("P2.processMessage what=" + message.what);
                 switch(message.what) {
                 case(CMD_3):
                     break;
@@ -1779,7 +1778,7 @@
 
         @Override
         protected void onHalting() {
-            log("halting");
+            tlog("halting");
             synchronized (this) {
                 this.notifyAll();
             }
@@ -1852,11 +1851,11 @@
         if (DBG) tlog("testStateMachineSharedThread X");
     }
 
-    private void tlog(String s) {
+    private static void tlog(String s) {
         Log.d(TAG, s);
     }
 
-    private void tloge(String s) {
+    private static void tloge(String s) {
         Log.e(TAG, s);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 380fcd4..0a3f0c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.support.annotation.NonNull;
 import android.text.Spannable;
@@ -43,12 +44,13 @@
 import android.text.TextUtils;
 import android.text.style.TtsSpan;
 import android.util.Log;
-import android.util.LruCache;
 
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 
 public class AccessPoint implements Comparable<AccessPoint> {
@@ -81,7 +83,9 @@
      *  For now this data is used only with Verbose Logging so as to show the band and number
      *  of BSSIDs on which that network is seen.
      */
-    public LruCache<String, ScanResult> mScanResultCache = new LruCache<String, ScanResult>(32);
+    private final ConcurrentHashMap<String, ScanResult> mScanResultCache =
+            new ConcurrentHashMap<String, ScanResult>(32);
+    private static final long MAX_SCAN_RESULT_AGE_MS = 15000;
 
     private static final String KEY_NETWORKINFO = "key_networkinfo";
     private static final String KEY_WIFIINFO = "key_wifiinfo";
@@ -149,7 +153,7 @@
         if (savedState.containsKey(KEY_SCANRESULTCACHE)) {
             ArrayList<ScanResult> scanResultArrayList =
                     savedState.getParcelableArrayList(KEY_SCANRESULTCACHE);
-            mScanResultCache.evictAll();
+            mScanResultCache.clear();
             for (ScanResult result : scanResultArrayList) {
                 mScanResultCache.put(result.BSSID, result);
             }
@@ -233,6 +237,17 @@
         return builder.append(')').toString();
     }
 
+    private void evictOldScanResults() {
+        long nowMs = SystemClock.elapsedRealtime();
+        for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
+            ScanResult result = iter.next();
+            // result timestamp is in microseconds
+            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MS) {
+                iter.remove();
+            }
+        }
+    }
+
     public boolean matches(ScanResult result) {
         return ssid.equals(result.SSID) && security == getSecurity(result);
     }
@@ -268,8 +283,9 @@
     }
 
     public int getRssi() {
+        evictOldScanResults();
         int rssi = Integer.MIN_VALUE;
-        for (ScanResult result : mScanResultCache.snapshot().values()) {
+        for (ScanResult result : mScanResultCache.values()) {
             if (result.level > rssi) {
                 rssi = result.level;
             }
@@ -279,8 +295,9 @@
     }
 
     public long getSeen() {
+        evictOldScanResults();
         long seen = 0;
-        for (ScanResult result : mScanResultCache.snapshot().values()) {
+        for (ScanResult result : mScanResultCache.values()) {
             if (result.timestamp > seen) {
                 seen = result.timestamp;
             }
@@ -505,9 +522,9 @@
         int numBlackListed = 0;
         int n24 = 0; // Number scan results we included in the string
         int n5 = 0; // Number scan results we included in the string
-        Map<String, ScanResult> list = mScanResultCache.snapshot();
+        evictOldScanResults();
         // TODO: sort list by RSSI or age
-        for (ScanResult result : list.values()) {
+        for (ScanResult result : mScanResultCache.values()) {
 
             if (result.frequency >= LOWER_FREQ_5GHZ
                     && result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -684,8 +701,9 @@
         savedState.putInt(KEY_PSKTYPE, pskType);
         if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig);
         savedState.putParcelable(KEY_WIFIINFO, mInfo);
+        evictOldScanResults();
         savedState.putParcelableArrayList(KEY_SCANRESULTCACHE,
-                new ArrayList<ScanResult>(mScanResultCache.snapshot().values()));
+                new ArrayList<ScanResult>(mScanResultCache.values()));
         if (mNetworkInfo != null) {
             savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
         }
@@ -697,9 +715,6 @@
 
     boolean update(ScanResult result) {
         if (matches(result)) {
-            /* Update the LRU timestamp, if BSSID exists */
-            mScanResultCache.get(result.BSSID);
-
             /* Add or update the scan result for the BSSID */
             mScanResultCache.put(result.BSSID, result);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f8fb1e5..920df2e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -28,12 +28,9 @@
 import android.net.NetworkPolicyManager;
 import android.net.Uri;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
-import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.BackupUtils;
@@ -41,34 +38,19 @@
 
 import com.android.internal.widget.LockPatternUtils;
 
-import libcore.io.IoUtils;
-
 import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.CharArrayReader;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.zip.CRC32;
 
 /**
@@ -86,10 +68,11 @@
     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";
+    private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 6;
+    private static final int STATE_VERSION = 7;
 
     // Versioning of the Network Policies backup payload.
     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,8 +89,9 @@
     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_WIFI_NEW_CONFIG  = 9;
 
-    private static final int STATE_SIZE             = 9; // The current number of state items
+    private static final int STATE_SIZE             = 10; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
@@ -117,16 +101,18 @@
             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
+            9,              // version 6 added STATE_NETWORK_POLICIES
+            STATE_SIZE      // version 7 added STATE_WIFI_NEW_CONFIG
     };
 
     // Versioning of the 'full backup' format
     // Increment this version any time a new item is added
-    private static final int FULL_BACKUP_VERSION = 5;
+    private static final int FULL_BACKUP_VERSION = 6;
     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 FULL_BACKUP_ADDED_WIFI_NEW = 6; // added "wifi_new_config" entry
 
     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
 
@@ -139,10 +125,6 @@
             Settings.NameValueTable.VALUE
     };
 
-    private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
-    private static final String FILE_WIFI_SUPPLICANT_TEMPLATE =
-            "/system/etc/wifi/wpa_supplicant.conf";
-
     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
     // use very late unicode character to quasi-guarantee last sort position.
     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
@@ -156,259 +138,17 @@
     // stored in the full-backup tarfile as well, so should not be changed.
     private static final String STAGE_FILE = "flattened-data";
 
-    // Delay in milliseconds between the restore operation and when we will bounce
-    // wifi in order to rewrite the supplicant config etc.
-    private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
-
     private SettingsHelper mSettingsHelper;
-    private WifiManager mWfm;
-    private String mWifiConfigFile;
 
-    // Chain of asynchronous operations used when rewriting the wifi supplicant config file
-    WifiDisableRunnable mWifiDisable = null;
-    WifiRestoreRunnable mWifiRestore = null;
-    int mRetainedWifiState; // used only during config file rewrite
-
-    // Class for capturing a network definition from the wifi supplicant config file
-    static class Network {
-        String ssid = "";  // equals() and hashCode() need these to be non-null
-        String key_mgmt = "";
-        boolean certUsed = false;
-        boolean hasWepKey = false;
-        boolean isEap = false;
-        final ArrayList<String> rawLines = new ArrayList<String>();
-
-        public static Network readFromStream(BufferedReader in) {
-            final Network n = new Network();
-            String line;
-            try {
-                while (in.ready()) {
-                    line = in.readLine();
-                    if (line == null || line.startsWith("}")) {
-                        break;
-                    }
-                    n.rememberLine(line);
-                }
-            } catch (IOException e) {
-                return null;
-            }
-            return n;
-        }
-
-        void rememberLine(String line) {
-            // can't rely on particular whitespace patterns so strip leading/trailing
-            line = line.trim();
-            if (line.isEmpty()) return; // only whitespace; drop the line
-            rawLines.add(line);
-
-            // remember the ssid and key_mgmt lines for duplicate culling
-            if (line.startsWith("ssid=")) {
-                ssid = line;
-            } else if (line.startsWith("key_mgmt=")) {
-                key_mgmt = line;
-                if (line.contains("EAP")) {
-                    isEap = true;
-                }
-            } else if (line.startsWith("client_cert=")) {
-                certUsed = true;
-            } else if (line.startsWith("ca_cert=")) {
-                certUsed = true;
-            } else if (line.startsWith("ca_path=")) {
-                certUsed = true;
-            } else if (line.startsWith("wep_")) {
-                hasWepKey = true;
-            } else if (line.startsWith("eap=")) {
-                isEap = true;
-            }
-        }
-
-        public void write(Writer w) throws IOException {
-            w.write("\nnetwork={\n");
-            for (String line : rawLines) {
-                w.write("\t" + line + "\n");
-            }
-            w.write("}\n");
-        }
-
-        public void dump() {
-            Log.v(TAG, "network={");
-            for (String line : rawLines) {
-                Log.v(TAG, "   " + line);
-            }
-            Log.v(TAG, "}");
-        }
-
-        // Calculate the equivalent of WifiConfiguration's configKey()
-        public String configKey() {
-            if (ssid == null) {
-                // No SSID => malformed network definition
-                return null;
-            }
-
-            final String bareSsid = ssid.substring(ssid.indexOf('=') + 1);
-
-            final BitSet types = new BitSet();
-            if (key_mgmt == null) {
-                // no key_mgmt specified; this is defined as equivalent to "WPA-PSK WPA-EAP"
-                types.set(KeyMgmt.WPA_PSK);
-                types.set(KeyMgmt.WPA_EAP);
-            } else {
-                // Need to parse the key_mgmt line
-                final String bareKeyMgmt = key_mgmt.substring(key_mgmt.indexOf('=') + 1);
-                String[] typeStrings = bareKeyMgmt.split("\\s+");
-
-                // Parse out all the key management regimes permitted for this network.  The literal
-                // strings here are the standard values permitted in wpa_supplicant.conf.
-                for (int i = 0; i < typeStrings.length; i++) {
-                    final String ktype = typeStrings[i];
-                    if (ktype.equals("WPA-PSK")) {
-                        Log.v(TAG, "  + setting WPA_PSK bit");
-                        types.set(KeyMgmt.WPA_PSK);
-                    } else if (ktype.equals("WPA-EAP")) {
-                        Log.v(TAG, "  + setting WPA_EAP bit");
-                        types.set(KeyMgmt.WPA_EAP);
-                    } else if (ktype.equals("IEEE8021X")) {
-                        Log.v(TAG, "  + setting IEEE8021X bit");
-                        types.set(KeyMgmt.IEEE8021X);
-                    }
-                }
-            }
-
-            // Now build the canonical config key paralleling the WifiConfiguration semantics
-            final String key;
-            if (types.get(KeyMgmt.WPA_PSK)) {
-                key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK];
-            } else if (types.get(KeyMgmt.WPA_EAP) || types.get(KeyMgmt.IEEE8021X)) {
-                key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP];
-            } else if (hasWepKey) {
-                key = bareSsid + "WEP";  // hardcoded this way in WifiConfiguration
-            } else {
-                key = bareSsid + KeyMgmt.strings[KeyMgmt.NONE];
-            }
-            return key;
-        }
-
-        // Same approach as Pair.equals() and Pair.hashCode()
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) return true;
-            if (!(o instanceof Network)) return false;
-            final Network other;
-            try {
-                other = (Network) o;
-            } catch (ClassCastException e) {
-                return false;
-            }
-            return ssid.equals(other.ssid) && key_mgmt.equals(other.key_mgmt);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 17;
-            result = 31 * result + ssid.hashCode();
-            result = 31 * result + key_mgmt.hashCode();
-            return result;
-        }
-    }
-
-    boolean networkInWhitelist(Network net, List<WifiConfiguration> whitelist) {
-        final String netConfigKey = net.configKey();
-        final int N = whitelist.size();
-        for (int i = 0; i < N; i++) {
-            if (Objects.equals(netConfigKey, whitelist.get(i).configKey(true))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Ingest multiple wifi config file fragments, looking for network={} blocks
-    // and eliminating duplicates
-    class WifiNetworkSettings {
-        // One for fast lookup, one for maintaining ordering
-        final HashSet<Network> mKnownNetworks = new HashSet<Network>();
-        final ArrayList<Network> mNetworks = new ArrayList<Network>(8);
-
-        public void readNetworks(BufferedReader in, List<WifiConfiguration> whitelist,
-                boolean acceptEapNetworks) {
-            try {
-                String line;
-                while (in.ready()) {
-                    line = in.readLine();
-                    if (line != null) {
-                        // Parse out 'network=' decls so we can ignore duplicates
-                        if (line.startsWith("network")) {
-                            Network net = Network.readFromStream(in);
-                            if (whitelist != null) {
-                                if (!networkInWhitelist(net, whitelist)) {
-                                    if (DEBUG_BACKUP) {
-                                        Log.v(TAG, "Network not in whitelist, skipping: "
-                                                + net.ssid + " / " + net.key_mgmt);
-                                    }
-                                    continue;
-                                }
-                            }
-                            // Don't propagate EAP network definitions
-                            if (net.isEap && !acceptEapNetworks) {
-                                if (DEBUG_BACKUP) {
-                                    Log.v(TAG, "Skipping EAP network " + net.ssid + " / " + net.key_mgmt);
-                                }
-                                continue;
-                            }
-                            if (!mKnownNetworks.contains(net)) {
-                                if (DEBUG_BACKUP) {
-                                    Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
-                                }
-                                mKnownNetworks.add(net);
-                                mNetworks.add(net);
-                            } else {
-                                if (DEBUG_BACKUP) {
-                                    Log.v(TAG, "Dupe; skipped " + net.ssid + " / " + net.key_mgmt);
-                                }
-                            }
-                        }
-                    }
-                }
-            } catch (IOException e) {
-                // whatever happened, we're done now
-            }
-        }
-
-        public void write(Writer w) throws IOException {
-            for (Network net : mNetworks) {
-                if (net.certUsed) {
-                    // Networks that use certificates for authentication can't be restored
-                    // because the certificates they need don't get restored (because they
-                    // are stored in keystore, and can't be restored)
-                    continue;
-                }
-
-                if (net.isEap) {
-                    // Similarly, omit EAP network definitions to avoid propagating
-                    // controlled enterprise network definitions.
-                    continue;
-                }
-
-                net.write(w);
-            }
-        }
-
-        public void dump() {
-            for (Network net : mNetworks) {
-                net.dump();
-            }
-        }
-    }
+    private WifiManager mWifiManager;
 
     @Override
     public void onCreate() {
         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
 
         mSettingsHelper = new SettingsHelper(this);
+        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
         super.onCreate();
-
-        WifiManager mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
-        if (mWfm != null) mWifiConfigFile = mWfm.getConfigFile();
     }
 
     @Override
@@ -420,10 +160,9 @@
         byte[] globalSettingsData = getGlobalSettings();
         byte[] lockSettingsData   = getLockSettings();
         byte[] locale = mSettingsHelper.getLocaleData();
-        byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
-        byte[] wifiConfigData = getFileData(mWifiConfigFile);
         byte[] softApConfigData = getSoftAPConfiguration();
         byte[] netPoliciesData = getNetworkPolicies();
+        byte[] wifiFullConfigData = getNewWifiConfigData();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
@@ -435,12 +174,8 @@
                 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
         stateChecksums[STATE_LOCALE] =
                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
-        stateChecksums[STATE_WIFI_SUPPLICANT] =
-                writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
-                        wifiSupplicantData, data);
-        stateChecksums[STATE_WIFI_CONFIG] =
-                writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
-                        data);
+        stateChecksums[STATE_WIFI_SUPPLICANT] = 0;
+        stateChecksums[STATE_WIFI_CONFIG] = 0;
         stateChecksums[STATE_LOCK_SETTINGS] =
                 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
                         lockSettingsData, data);
@@ -450,112 +185,13 @@
         stateChecksums[STATE_NETWORK_POLICIES] =
                 writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
                         netPoliciesData, data);
+        stateChecksums[STATE_WIFI_NEW_CONFIG] =
+                writeIfChanged(stateChecksums[STATE_WIFI_NEW_CONFIG], KEY_WIFI_NEW_CONFIG,
+                        wifiFullConfigData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
 
-    class WifiDisableRunnable implements Runnable {
-        final WifiRestoreRunnable mNextPhase;
-
-        public WifiDisableRunnable(WifiRestoreRunnable next) {
-            mNextPhase = next;
-        }
-
-        @Override
-        public void run() {
-            if (DEBUG_BACKUP) {
-                Log.v(TAG, "Disabling wifi during restore");
-            }
-            final ContentResolver cr = getContentResolver();
-            final int scanAlways = Settings.Global.getInt(cr,
-                    Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
-            final int retainedWifiState = enableWifi(false);
-            if (scanAlways != 0) {
-                Settings.Global.putInt(cr,
-                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
-            }
-
-            // Tell the final stage how to clean up after itself
-            mNextPhase.setPriorState(retainedWifiState, scanAlways);
-
-            // And run it after a modest pause to give broadcasts and content
-            // observers time an opportunity to run on this looper thread, so
-            // that the wifi stack actually goes all the way down.
-            new Handler(getMainLooper()).postDelayed(mNextPhase, 2500);
-        }
-    }
-
-    class WifiRestoreRunnable implements Runnable {
-        private byte[] restoredSupplicantData;
-        private byte[] restoredWifiConfigFile;
-        private int retainedWifiState;  // provided by disable stage
-        private int scanAlways; // provided by disable stage
-
-        void setPriorState(int retainedState, int always) {
-            retainedWifiState = retainedState;
-            scanAlways = always;
-        }
-
-        void incorporateWifiSupplicant(BackupDataInput data) {
-            restoredSupplicantData = new byte[data.getDataSize()];
-            if (restoredSupplicantData.length <= 0) return;
-            try {
-                data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
-            } catch (IOException e) {
-                Log.w(TAG, "Unable to read supplicant data");
-                restoredSupplicantData = null;
-            }
-        }
-
-        void incorporateWifiConfigFile(BackupDataInput data) {
-            restoredWifiConfigFile = new byte[data.getDataSize()];
-            if (restoredWifiConfigFile.length <= 0) return;
-            try {
-                data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
-            } catch (IOException e) {
-                Log.w(TAG, "Unable to read config file");
-                restoredWifiConfigFile = null;
-            }
-        }
-
-        @Override
-        public void run() {
-            if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
-                if (DEBUG_BACKUP) {
-                    Log.v(TAG, "Applying restored wifi data");
-                }
-                if (restoredSupplicantData != null) {
-                    restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
-                            restoredSupplicantData, restoredSupplicantData.length);
-                    FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
-                            FileUtils.S_IRUSR | FileUtils.S_IWUSR
-                            | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-                            Process.myUid(), Process.WIFI_UID);
-                }
-                if (restoredWifiConfigFile != null) {
-                    restoreFileData(mWifiConfigFile,
-                            restoredWifiConfigFile, restoredWifiConfigFile.length);
-                }
-                // restore the previous WIFI state.
-                if (scanAlways != 0) {
-                    Settings.Global.putInt(getContentResolver(),
-                            Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways);
-                }
-                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
-                        || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
-            }
-        }
-    }
-
-    // Instantiate the wifi-config restore runnable, scheduling it for execution
-    // a minute hence
-    void initWifiRestoreIfNecessary() {
-        if (mWifiRestore == null) {
-            mWifiRestore = new WifiRestoreRunnable();
-            mWifiDisable = new WifiDisableRunnable(mWifiRestore);
-        }
-    }
-
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode,
             ParcelFileDescriptor newState) throws IOException {
@@ -563,6 +199,8 @@
         HashSet<String> movedToGlobal = new HashSet<String>();
         Settings.System.getMovedToGlobalSettings(movedToGlobal);
         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
+        byte[] restoredWifiSupplicantData = null;
+        byte[] restoredWifiIpConfigData = null;
 
         while (data.readNextHeader()) {
             final String key = data.getKey();
@@ -582,8 +220,8 @@
                     break;
 
                 case KEY_WIFI_SUPPLICANT :
-                    initWifiRestoreIfNecessary();
-                    mWifiRestore.incorporateWifiSupplicant(data);
+                    restoredWifiSupplicantData = new byte[size];
+                    data.readEntityData(restoredWifiSupplicantData, 0, size);
                     break;
 
                 case KEY_LOCALE :
@@ -593,8 +231,8 @@
                     break;
 
                 case KEY_WIFI_CONFIG :
-                    initWifiRestoreIfNecessary();
-                    mWifiRestore.incorporateWifiConfigFile(data);
+                    restoredWifiIpConfigData = new byte[size];
+                    data.readEntityData(restoredWifiIpConfigData, 0, size);
                     break;
 
                 case KEY_LOCK_SETTINGS :
@@ -613,23 +251,22 @@
                     restoreNetworkPolicies(netPoliciesData);
                     break;
 
+                case KEY_WIFI_NEW_CONFIG:
+                    byte[] restoredWifiNewConfigData = new byte[size];
+                    data.readEntityData(restoredWifiNewConfigData, 0, size);
+                    restoreNewWifiConfigData(restoredWifiNewConfigData);
+                    break;
+
                 default :
                     data.skipEntityData();
 
             }
         }
 
-        // If we have wifi data to restore, post a runnable to perform the
-        // bounce-and-update operation a little ways in the future.  The
-        // 'disable' runnable brings down the stack and remembers its state,
-        // and in turn schedules the 'restore' runnable to do the rewrite
-        // and cleanup operations.
-        if (mWifiRestore != null) {
-            long wifiBounceDelayMillis = Settings.Global.getLong(
-                    getContentResolver(),
-                    Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
-                    WIFI_BOUNCE_DELAY_MILLIS);
-            new Handler(getMainLooper()).postDelayed(mWifiDisable, wifiBounceDelayMillis);
+        // Do this at the end so that we also pull in the ipconfig data.
+        if (restoredWifiSupplicantData != null) {
+            restoreSupplicantWifiConfigData(
+                    restoredWifiSupplicantData, restoredWifiIpConfigData);
         }
     }
 
@@ -640,10 +277,9 @@
         byte[] globalSettingsData = getGlobalSettings();
         byte[] lockSettingsData   = getLockSettings();
         byte[] locale = mSettingsHelper.getLocaleData();
-        byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
-        byte[] wifiConfigData = getFileData(mWifiConfigFile);
         byte[] softApConfigData = getSoftAPConfiguration();
         byte[] netPoliciesData = getNetworkPolicies();
+        byte[] wifiFullConfigData = getNewWifiConfigData();
 
         // Write the data to the staging file, then emit that as our tarfile
         // representation of the backed-up settings.
@@ -673,14 +309,6 @@
             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");
-            }
-            out.writeInt(wifiSupplicantData.length);
-            out.write(wifiSupplicantData);
-            if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
-            out.writeInt(wifiConfigData.length);
-            out.write(wifiConfigData);
             if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
             out.writeInt(lockSettingsData.length);
             out.write(lockSettingsData);
@@ -690,6 +318,11 @@
             if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
             out.writeInt(netPoliciesData.length);
             out.write(netPoliciesData);
+            if (DEBUG_BACKUP) {
+                Log.d(TAG, wifiFullConfigData.length + " bytes of wifi config data");
+            }
+            out.writeInt(wifiFullConfigData.length);
+            out.write(wifiFullConfigData);
 
             out.flush();    // also flushes downstream
 
@@ -750,27 +383,21 @@
             in.readFully(buffer, 0, nBytes);
             mSettingsHelper.setLocaleData(buffer, nBytes);
 
-            // wifi supplicant
-            nBytes = in.readInt();
-            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
-            if (nBytes > buffer.length) buffer = new byte[nBytes];
-            in.readFully(buffer, 0, nBytes);
-            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,
-                    Process.myUid(), Process.WIFI_UID);
-            // retain the previous WIFI state.
-            enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
-                    || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            // Restore older backups performing the necessary migrations.
+            if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
+                // wifi supplicant
+                int supplicant_size = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, supplicant_size + " bytes of wifi supplicant data");
+                byte[] supplicant_buffer = new byte[supplicant_size];
+                in.readFully(supplicant_buffer, 0, supplicant_size);
 
-            // wifi config
-            nBytes = in.readInt();
-            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
-            if (nBytes > buffer.length) buffer = new byte[nBytes];
-            in.readFully(buffer, 0, nBytes);
-            restoreFileData(mWifiConfigFile, buffer, nBytes);
+                // ip config
+                int ipconfig_size = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, ipconfig_size + " bytes of ip config data");
+                byte[] ipconfig_buffer = new byte[ipconfig_size];
+                in.readFully(ipconfig_buffer, 0, nBytes);
+                restoreSupplicantWifiConfigData(supplicant_buffer, ipconfig_buffer);
+            }
 
             if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
                 nBytes = in.readInt();
@@ -801,6 +428,15 @@
                     restoreNetworkPolicies(buffer);
                 }
             }
+            // Restore full wifi config data
+            if (version >= FULL_BACKUP_ADDED_WIFI_NEW) {
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of full wifi config data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                in.readFully(buffer, 0, nBytes);
+                restoreNewWifiConfigData(buffer);
+            }
+
             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
         } else {
             data.close();
@@ -1114,146 +750,16 @@
         return result;
     }
 
-    private byte[] getFileData(String filename) {
-        InputStream is = null;
-        try {
-            File file = new File(filename);
-            is = new FileInputStream(file);
-
-            //Will truncate read on a very long file,
-            //should not happen for a config file
-            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) {
-                offset += numRead;
-            }
-
-            //read failure
-            if (offset < bytes.length) {
-                Log.w(TAG, "Couldn't backup " + filename);
-                return EMPTY_DATA;
-            }
-            return bytes;
-        } catch (IOException ioe) {
-            Log.w(TAG, "Couldn't backup " + filename);
-            return EMPTY_DATA;
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                }
-            }
+    private void restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes) {
+        if (DEBUG_BACKUP) {
+            Log.v(TAG, "Applying restored supplicant wifi data");
         }
-    }
-
-    private void restoreFileData(String filename, byte[] bytes, int size) {
-        try {
-            File file = new File(filename);
-            if (file.exists()) file.delete();
-
-            OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
-            os.write(bytes, 0, size);
-            os.close();
-        } catch (IOException ioe) {
-            Log.w(TAG, "Couldn't restore " + filename);
-        }
-    }
-
-
-    private byte[] getWifiSupplicant(String filename) {
-        BufferedReader br = null;
-        try {
-            File file = new File(filename);
-            if (!file.exists()) {
-                return EMPTY_DATA;
-            }
-
-            WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
-            List<WifiConfiguration> configs = wifi.getConfiguredNetworks();
-
-            WifiNetworkSettings fromFile = new WifiNetworkSettings();
-            br = new BufferedReader(new FileReader(file));
-            fromFile.readNetworks(br, configs, false);
-
-            // Write the parsed networks into a packed byte array
-            if (fromFile.mKnownNetworks.size() > 0) {
-                ByteArrayOutputStream bos = new ByteArrayOutputStream();
-                OutputStreamWriter out = new OutputStreamWriter(bos);
-                fromFile.write(out);
-                out.flush();
-                return bos.toByteArray();
-            } else {
-                return EMPTY_DATA;
-            }
-        } catch (IOException ioe) {
-            Log.w(TAG, "Couldn't backup " + filename);
-            return EMPTY_DATA;
-        } finally {
-            IoUtils.closeQuietly(br);
-        }
-    }
-
-    private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
-        try {
-            WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
-
-            File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
-            if (supplicantFile.exists()) {
-                // Retain the existing APs; we'll append the restored ones to them
-                BufferedReader in = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT));
-                supplicantImage.readNetworks(in, null, true);
-                in.close();
-
-                supplicantFile.delete();
-            }
-
-            // Incorporate the restore AP information
-            if (size > 0) {
-                char[] restoredAsBytes = new char[size];
-                for (int i = 0; i < size; i++) restoredAsBytes[i] = (char) bytes[i];
-                BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
-                supplicantImage.readNetworks(in, null, false);
-
-                if (DEBUG_BACKUP) {
-                    Log.v(TAG, "Final AP list:");
-                    supplicantImage.dump();
-                }
-            }
-
-            // Install the correct default template
-            BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
-            copyWifiSupplicantTemplate(bw);
-
-            // Write the restored supplicant config and we're done
-            supplicantImage.write(bw);
-            bw.close();
-        } catch (IOException ioe) {
-            Log.w(TAG, "Couldn't restore " + filename);
-        }
-    }
-
-    private void copyWifiSupplicantTemplate(BufferedWriter bw) {
-        try {
-            BufferedReader br = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT_TEMPLATE));
-            char[] temp = new char[1024];
-            int size;
-            while ((size = br.read(temp)) > 0) {
-                bw.write(temp, 0, size);
-            }
-            br.close();
-        } catch (IOException ioe) {
-            Log.w(TAG, "Couldn't copy wpa_supplicant file");
-        }
+        mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
     }
 
     private byte[] getSoftAPConfiguration() {
-        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
         try {
-            return wifiManager.getWifiApConfiguration().getBytesForBackup();
+            return mWifiManager.getWifiApConfiguration().getBytesForBackup();
         } catch (IOException ioe) {
             Log.e(TAG, "Failed to marshal SoftAPConfiguration" + ioe.getMessage());
             return new byte[0];
@@ -1261,12 +767,11 @@
     }
 
     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);
+            mWifiManager.setWifiApConfiguration(config);
         } catch (IOException | BackupUtils.BadVersionException e) {
             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
         }
@@ -1300,6 +805,17 @@
         return baos.toByteArray();
     }
 
+    private byte[] getNewWifiConfigData() {
+        return mWifiManager.retrieveBackupData();
+    }
+
+    private void restoreNewWifiConfigData(byte[] bytes) {
+        if (DEBUG_BACKUP) {
+            Log.v(TAG, "Applying restored wifi data");
+        }
+        mWifiManager.restoreBackupData(bytes);
+    }
+
     private void restoreNetworkPolicies(byte[] data) {
         NetworkPolicyManager networkPolicyManager =
                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
@@ -1358,18 +874,4 @@
                 | ((in[pos + 3] & 0xFF) <<  0);
         return result;
     }
-
-    private int enableWifi(boolean enable) {
-        if (mWfm == null) {
-            mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
-        }
-        if (mWfm != null) {
-            int state = mWfm.getWifiState();
-            mWfm.setWifiEnabled(enable);
-            return state;
-        } else {
-            Log.e(TAG, "Failed to fetch WifiManager instance");
-        }
-        return WifiManager.WIFI_STATE_UNKNOWN;
-    }
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index bdbd066..8f16504 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -216,7 +216,7 @@
     private CountDownLatch mConnectedSignal = new CountDownLatch(1);
 
     private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
-            new RemoteCallbackList<INetworkManagementEventObserver>();
+            new RemoteCallbackList<>();
 
     private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
 
@@ -289,7 +289,7 @@
     private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
 
     private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
-            new RemoteCallbackList<INetworkActivityListener>();
+            new RemoteCallbackList<>();
     private boolean mNetworkActive;
 
     /**
@@ -1172,7 +1172,7 @@
 
     private ArrayList<String> readRouteList(String filename) {
         FileInputStream fstream = null;
-        ArrayList<String> list = new ArrayList<String>();
+        ArrayList<String> list = new ArrayList<>();
 
         try {
             fstream = new FileInputStream(filename);
@@ -1296,7 +1296,7 @@
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
-        List<RouteInfo> routes = new ArrayList<RouteInfo>();
+        List<RouteInfo> routes = new ArrayList<>();
         // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
         // suitable to use as a route destination.
         routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface));
@@ -1357,7 +1357,7 @@
     }
 
     private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) {
-        ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size());
+        ArrayList<InterfaceAddress> filtered = new ArrayList<>(addresses.size());
         for (InterfaceAddress ia : addresses) {
             if (!ia.getAddress().isLinkLocalAddress())
                 filtered.add(ia);
@@ -1470,122 +1470,6 @@
         }
     }
 
-    /**
-     * Private method used to call execute for a command given the provided arguments.
-     *
-     * This function checks the returned NativeDaemonEvent for the provided expected response code
-     * and message.  If either of these is not correct, an error is logged.
-     *
-     * @param String command The command to execute.
-     * @param Object[] args If needed, arguments for the command to execute.
-     * @param int expectedResponseCode The code expected to be returned in the corresponding event.
-     * @param String expectedResponseMessage The message expected in the returned event.
-     * @param String logMsg The message to log as an error (TAG will be applied).
-     */
-    private void executeOrLogWithMessage(String command, Object[] args,
-            int expectedResponseCode, String expectedResponseMessage, String logMsg)
-            throws NativeDaemonConnectorException {
-        NativeDaemonEvent event = mConnector.execute(command, args);
-        if (event.getCode() != expectedResponseCode
-                || !event.getMessage().equals(expectedResponseMessage)) {
-            Log.e(TAG, logMsg + ": event = " + event);
-        }
-    }
-
-    @Override
-    public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] args;
-        String logMsg = "startAccessPoint Error setting up softap";
-        try {
-            if (wifiConfig == null) {
-                args = new Object[] {"set", wlanIface};
-            } else {
-                args = new Object[] {"set", wlanIface, wifiConfig.SSID,
-                        "broadcast", Integer.toString(wifiConfig.apChannel),
-                        getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)};
-            }
-            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
-                    SOFT_AP_COMMAND_SUCCESS, logMsg);
-
-            logMsg = "startAccessPoint Error starting softap";
-            args = new Object[] {"startap"};
-            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
-                    SOFT_AP_COMMAND_SUCCESS, logMsg);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
-    private static String getSecurityType(WifiConfiguration wifiConfig) {
-        switch (wifiConfig.getAuthType()) {
-            case KeyMgmt.WPA_PSK:
-                return "wpa-psk";
-            case KeyMgmt.WPA2_PSK:
-                return "wpa2-psk";
-            default:
-                return "open";
-        }
-    }
-
-    /* @param mode can be "AP", "STA" or "P2P" */
-    @Override
-    public void wifiFirmwareReload(String wlanIface, String mode) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] args = {"fwreload", wlanIface, mode};
-        String logMsg = "wifiFirmwareReload Error reloading "
-                + wlanIface + " fw in " + mode + " mode";
-        try {
-            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
-                    SOFT_AP_COMMAND_SUCCESS, logMsg);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-
-        // Ensure that before we return from this command, any asynchronous
-        // notifications generated before the command completed have been
-        // processed by all NetworkManagementEventObservers.
-        mConnector.waitForCallbacks();
-    }
-
-    @Override
-    public void stopAccessPoint(String wlanIface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] args = {"stopap"};
-        String logMsg = "stopAccessPoint Error stopping softap";
-
-        try {
-            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
-                    SOFT_AP_COMMAND_SUCCESS, logMsg);
-            wifiFirmwareReload(wlanIface, "STA");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
-    @Override
-    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] args;
-        String logMsg = "startAccessPoint Error setting up softap";
-        try {
-            if (wifiConfig == null) {
-                args = new Object[] {"set", wlanIface};
-            } else {
-                // TODO: understand why this is set to "6" instead of
-                // Integer.toString(wifiConfig.apChannel) as in startAccessPoint
-                // TODO: should startAccessPoint call this instead of repeating code?
-                args = new Object[] {"set", wlanIface, wifiConfig.SSID,
-                        "broadcast", "6",
-                        getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)};
-            }
-            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
-                    SOFT_AP_COMMAND_SUCCESS, logMsg);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
     @Override
     public void addIdleTimer(String iface, int timeout, final int type) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 2807ec8..4d56468 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.net.DelayedDiskWrite;
 
 import java.io.BufferedInputStream;
@@ -34,7 +35,9 @@
 import java.io.DataOutputStream;
 import java.io.EOFException;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.Inet4Address;
 
@@ -67,7 +70,8 @@
         this(new DelayedDiskWrite());
     }
 
-    private boolean writeConfig(DataOutputStream out, int configKey,
+    @VisibleForTesting
+    public static boolean writeConfig(DataOutputStream out, int configKey,
                                 IpConfiguration config) throws IOException {
         boolean written = false;
 
@@ -171,12 +175,25 @@
         });
     }
 
-    public SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
-        SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
+    public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
+        BufferedInputStream bufferedInputStream;
+        try {
+            bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
+        } catch (FileNotFoundException e) {
+            // Return an empty array here because callers expect an empty array when the file is
+            // not present.
+            loge("Error opening configuration file: " + e);
+            return new SparseArray<>();
+        }
+        return readIpAndProxyConfigurations(bufferedInputStream);
+    }
 
+    public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
+            InputStream inputStream) {
+        SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
         DataInputStream in = null;
         try {
-            in = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
+            in = new DataInputStream(inputStream);
 
             int version = in.readInt();
             if (version != 2 && version != 1) {
@@ -327,11 +344,11 @@
         return networks;
     }
 
-    protected void loge(String s) {
+    protected static void loge(String s) {
         Log.e(TAG, s);
     }
 
-    protected void log(String s) {
+    protected static void log(String s) {
         Log.d(TAG, s);
     }
 }
diff --git a/services/tests/servicestests/src/android/net/IpUtilsTest.java b/services/tests/servicestests/src/android/net/util/IpUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/android/net/IpUtilsTest.java
rename to services/tests/servicestests/src/android/net/util/IpUtilsTest.java
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 9268a2b..b62ffac 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -89,7 +89,7 @@
 
     WifiInfo getConnectionInfo();
 
-    boolean setWifiEnabled(boolean enable);
+    boolean setWifiEnabled(String packageName, boolean enable);
 
     int getWifiEnabledState();
 
@@ -169,5 +169,11 @@
     void factoryReset();
 
     Network getCurrentNetwork();
+
+    byte[] retrieveBackupData();
+
+    void restoreBackupData(in byte[] data);
+
+    void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData);
 }
 
diff --git a/wifi/java/android/net/wifi/RttManager.aidl b/wifi/java/android/net/wifi/RttManager.aidl
index 5c6d447..9479cf0 100644
--- a/wifi/java/android/net/wifi/RttManager.aidl
+++ b/wifi/java/android/net/wifi/RttManager.aidl
@@ -15,4 +15,7 @@
  */
 
 package android.net.wifi;
-parcelable RttManager.RttCapabilities;
\ No newline at end of file
+
+parcelable RttManager.RttCapabilities;
+parcelable RttManager.ParcelableRttResults;
+parcelable RttManager.ParcelableRttParams;
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 590ff1b..c6f1f68 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -472,6 +472,34 @@
             preamble = PREAMBLE_HT;
             bandwidth = RTT_BW_20_SUPPORT;
         }
+
+        /**
+         * {@hide}
+         */
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("deviceType=" + deviceType);
+            sb.append(", requestType=" + requestType);
+            sb.append(", secure=" + secure);
+            sb.append(", bssid=" + bssid);
+            sb.append(", frequency=" + frequency);
+            sb.append(", channelWidth=" + channelWidth);
+            sb.append(", centerFreq0=" + centerFreq0);
+            sb.append(", centerFreq1=" + centerFreq1);
+            sb.append(", num_samples=" + num_samples);
+            sb.append(", num_retries=" + num_retries);
+            sb.append(", numberBurst=" + numberBurst);
+            sb.append(", interval=" + interval);
+            sb.append(", numSamplesPerBurst=" + numSamplesPerBurst);
+            sb.append(", numRetriesPerMeasurementFrame=" + numRetriesPerMeasurementFrame);
+            sb.append(", numRetriesPerFTMR=" + numRetriesPerFTMR);
+            sb.append(", LCIRequest=" + LCIRequest);
+            sb.append(", LCRRequest=" + LCRRequest);
+            sb.append(", burstTimeout=" + burstTimeout);
+            sb.append(", preamble=" + preamble);
+            sb.append(", bandwidth=" + bandwidth);
+            return sb.toString();
+        }
     }
 
     /** pseudo-private class used to parcel arguments */
@@ -727,6 +755,51 @@
             mResults = results;
         }
 
+        /**
+         * {@hide}
+         */
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < mResults.length; ++i) {
+                sb.append("[" + i + "]: ");
+                sb.append("bssid=" + mResults[i].bssid);
+                sb.append(", burstNumber=" + mResults[i].burstNumber);
+                sb.append(", measurementFrameNumber=" + mResults[i].measurementFrameNumber);
+                sb.append(", successMeasurementFrameNumber="
+                        + mResults[i].successMeasurementFrameNumber);
+                sb.append(", frameNumberPerBurstPeer=" + mResults[i].frameNumberPerBurstPeer);
+                sb.append(", status=" + mResults[i].status);
+                sb.append(", requestType=" + mResults[i].requestType);
+                sb.append(", measurementType=" + mResults[i].measurementType);
+                sb.append(", retryAfterDuration=" + mResults[i].retryAfterDuration);
+                sb.append(", ts=" + mResults[i].ts);
+                sb.append(", rssi=" + mResults[i].rssi);
+                sb.append(", rssi_spread=" + mResults[i].rssi_spread);
+                sb.append(", rssiSpread=" + mResults[i].rssiSpread);
+                sb.append(", tx_rate=" + mResults[i].tx_rate);
+                sb.append(", txRate=" + mResults[i].txRate);
+                sb.append(", rxRate=" + mResults[i].rxRate);
+                sb.append(", rtt_ns=" + mResults[i].rtt_ns);
+                sb.append(", rtt=" + mResults[i].rtt);
+                sb.append(", rtt_sd_ns=" + mResults[i].rtt_sd_ns);
+                sb.append(", rttStandardDeviation=" + mResults[i].rttStandardDeviation);
+                sb.append(", rtt_spread_ns=" + mResults[i].rtt_spread_ns);
+                sb.append(", rttSpread=" + mResults[i].rttSpread);
+                sb.append(", distance_cm=" + mResults[i].distance_cm);
+                sb.append(", distance=" + mResults[i].distance);
+                sb.append(", distance_sd_cm=" + mResults[i].distance_sd_cm);
+                sb.append(", distanceStandardDeviation=" + mResults[i].distanceStandardDeviation);
+                sb.append(", distance_spread_cm=" + mResults[i].distance_spread_cm);
+                sb.append(", distanceSpread=" + mResults[i].distanceSpread);
+                sb.append(", burstDuration=" + mResults[i].burstDuration);
+                sb.append(", negotiatedBurstNum=" + mResults[i].negotiatedBurstNum);
+                sb.append(", LCI=" + mResults[i].LCI);
+                sb.append(", LCR=" + mResults[i].LCR);
+                sb.append(", secure=" + mResults[i].secure);
+            }
+            return sb.toString();
+        }
+
         /** Implement the Parcelable interface {@hide} */
         @Override
         public int describeContents() {
@@ -1295,4 +1368,3 @@
     }
 
 }
-
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index d3d5ea0..7c5276c 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -806,7 +806,7 @@
          * Quality network selection status String (for debug purpose). Use Quality network
          * selection status value as index to extec the corresponding debug string
          */
-        private static final String[] QUALITY_NETWORK_SELECTION_STATUS = {
+        public static final String[] QUALITY_NETWORK_SELECTION_STATUS = {
                 "NETWORK_SELECTION_ENABLED",
                 "NETWORK_SELECTION_TEMPORARY_DISABLED",
                 "NETWORK_SELECTION_PERMANENTLY_DISABLED"};
@@ -838,40 +838,51 @@
          */
         public static final int DISABLED_DNS_FAILURE = 5;
         /**
+         * This network is disabled because we started WPS
+         */
+        public static final int DISABLED_WPS_START = 6;
+        /**
          * This network is disabled because EAP-TLS failure
          */
-        public static final int DISABLED_TLS_VERSION_MISMATCH = 6;
+        public static final int DISABLED_TLS_VERSION_MISMATCH = 7;
         /**
-         * This network is disabled due to WifiManager disable it explicitly
+         * This network is disabled due to absence of user credentials
          */
-        public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 7;
+        public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 8;
         /**
          * This network is disabled because no Internet connected and user do not want
          */
-        public static final int DISABLED_NO_INTERNET = 8;
+        public static final int DISABLED_NO_INTERNET = 9;
         /**
          * This network is disabled due to WifiManager disable it explicitly
          */
-        public static final int DISABLED_BY_WIFI_MANAGER = 9;
+        public static final int DISABLED_BY_WIFI_MANAGER = 10;
+        /**
+         * This network is disabled due to user switching
+         */
+        public static final int DISABLED_DUE_TO_USER_SWITCH = 11;
         /**
          * This Maximum disable reason value
          */
-        public static final int NETWORK_SELECTION_DISABLED_MAX = 10;
+        public static final int NETWORK_SELECTION_DISABLED_MAX = 12;
 
         /**
          * Quality network selection disable reason String (for debug purpose)
          */
-        private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
+        public static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
                 "NETWORK_SELECTION_ENABLE",
                 "NETWORK_SELECTION_DISABLED_BAD_LINK", // deprecated
                 "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
                 "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
                 "NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
                 "NETWORK_SELECTION_DISABLED_DNS_FAILURE",
+                "NETWORK_SELECTION_DISABLED_WPS_START",
                 "NETWORK_SELECTION_DISABLED_TLS_VERSION",
                 "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS",
                 "NETWORK_SELECTION_DISABLED_NO_INTERNET",
-                "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER"};
+                "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER",
+                "NETWORK_SELECTION_DISABLED_BY_USER_SWITCH"
+        };
 
         /**
          * Invalid time stamp for network selection disable
@@ -1055,7 +1066,7 @@
             return mHasEverConnected;
         }
 
-        private NetworkSelectionStatus() {
+        public NetworkSelectionStatus() {
             // previously stored configs will not have this parameter, so we default to false.
             mHasEverConnected = false;
         };
@@ -1255,6 +1266,8 @@
             }
             mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
             mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+            setCandidate(source.getCandidate());
+            setCandidateScore(source.getCandidateScore());
             setConnectChoice(source.getConnectChoice());
             setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
             setHasEverConnected(source.getHasEverConnected());
@@ -1303,7 +1316,7 @@
      * @hide
      * network selection related member
      */
-    private final NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
+    private NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
 
     /**
      * @hide
@@ -1312,6 +1325,15 @@
     public NetworkSelectionStatus getNetworkSelectionStatus() {
         return mNetworkSelectionStatus;
     }
+
+    /**
+     * Set the network selection status
+     * @hide
+     */
+    public void setNetworkSelectionStatus(NetworkSelectionStatus status) {
+        mNetworkSelectionStatus = status;
+    }
+
     /**
      * @hide
      * Linked Configurations: represent the set of Wificonfigurations that are equivalent
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1633bd9c9..1aa4021 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -19,6 +19,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
@@ -563,6 +564,28 @@
     public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
 
     /**
+     * Activity Action: Show UI to get user approval to enable WiFi.
+     * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+     *           the name of the app requesting the action.
+     * <p>Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE";
+
+    /**
+     * Activity Action: Show UI to get user approval to disable WiFi.
+     * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+     *           the name of the app requesting the action.
+     * <p>Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
+
+    /**
      * Internally used Wi-Fi lock mode representing the case were no locks are held.
      * @hide
      */
@@ -1122,7 +1145,7 @@
 
     /**
      * @return true if this adapter supports Neighbour Awareness Network APIs
-     * @hide PROPOSED_NAN_API
+     * @hide
      */
     public boolean isNanSupported() {
         return isFeatureSupported(WIFI_FEATURE_NAN);
@@ -1445,7 +1468,7 @@
      */
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return mService.setWifiEnabled(enabled);
+            return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2739,4 +2762,41 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Retrieve the data to be backed to save the current state.
+     * @hide
+     */
+    public byte[] retrieveBackupData() {
+        try {
+            return mService.retrieveBackupData();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Restore state from the backed up data.
+     * @hide
+     */
+    public void restoreBackupData(byte[] data) {
+        try {
+            mService.restoreBackupData(data);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Restore state from the older version of back up data.
+     * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+     * @hide
+     */
+    public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
+        try {
+            mService.restoreSupplicantBackupData(supplicantData, ipConfigData);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java
index 23e3754..44544de 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java
@@ -16,15 +16,17 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
  * Defines a request object to configure a Wi-Fi NAN network. Built using
  * {@link ConfigRequest.Builder}. Configuration is requested using
- * {@link WifiNanManager#requestConfig(ConfigRequest)}. Note that the actual
- * achieved configuration may be different from the requested configuration -
- * since multiple applications may request different configurations.
+ * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}.
+ * Note that the actual achieved configuration may be different from the
+ * requested configuration - since different applications may request different
+ * configurations.
  *
  * @hide PROPOSED_NAN_API
  */
@@ -73,19 +75,28 @@
      */
     public final int mClusterHigh;
 
+    /**
+     * Indicates whether we want to get callbacks when our identity is changed.
+     *
+     * @hide
+     */
+    public final boolean mEnableIdentityChangeCallback;
+
     private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
-            int clusterHigh) {
+            int clusterHigh, boolean enableIdentityChangeCallback) {
         mSupport5gBand = support5gBand;
         mMasterPreference = masterPreference;
         mClusterLow = clusterLow;
         mClusterHigh = clusterHigh;
+        mEnableIdentityChangeCallback = enableIdentityChangeCallback;
     }
 
     @Override
     public String toString() {
         return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
                 + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
-                + mClusterHigh + "]";
+                + mClusterHigh + ", mEnableIdentityChangeCallback=" + mEnableIdentityChangeCallback
+                + "]";
     }
 
     @Override
@@ -99,6 +110,7 @@
         dest.writeInt(mMasterPreference);
         dest.writeInt(mClusterLow);
         dest.writeInt(mClusterHigh);
+        dest.writeInt(mEnableIdentityChangeCallback ? 1 : 0);
     }
 
     public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
@@ -113,7 +125,9 @@
             int masterPreference = in.readInt();
             int clusterLow = in.readInt();
             int clusterHigh = in.readInt();
-            return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh);
+            boolean enableIdentityChangeCallback = in.readInt() != 0;
+            return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh,
+                    enableIdentityChangeCallback);
         }
     };
 
@@ -130,9 +144,47 @@
         ConfigRequest lhs = (ConfigRequest) o;
 
         return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
+                && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh
+                && mEnableIdentityChangeCallback == lhs.mEnableIdentityChangeCallback;
+    }
+
+    /**
+     * Checks for equality of two configuration - but only considering their
+     * on-the-air NAN configuration impact.
+     *
+     * @param o Object to compare to.
+     * @return true if configuration objects have the same on-the-air
+     *         configuration, false otherwise.
+     *
+     * @hide
+     */
+    public boolean equalsOnTheAir(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof ConfigRequest)) {
+            return false;
+        }
+
+        ConfigRequest lhs = (ConfigRequest) o;
+
+        return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
                 && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh;
     }
 
+    /**
+     * Checks whether the configuration's settings which impact on-air behavior are non-default.
+     *
+     * @return true if any of the on-air-impacting settings are non-default.
+     *
+     * @hide
+     */
+    public boolean isNonDefaultOnTheAir() {
+        return mSupport5gBand || mMasterPreference != 0 || mClusterLow != CLUSTER_ID_MIN
+                || mClusterHigh != CLUSTER_ID_MAX;
+    }
+
     @Override
     public int hashCode() {
         int result = 17;
@@ -141,35 +193,63 @@
         result = 31 * result + mMasterPreference;
         result = 31 * result + mClusterLow;
         result = 31 * result + mClusterHigh;
+        result = 31 * result + (mEnableIdentityChangeCallback ? 1 : 0);
 
         return result;
     }
 
     /**
+     * Verifies that the contents of the ConfigRequest are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        if (mMasterPreference < 0) {
+            throw new IllegalArgumentException(
+                    "Master Preference specification must be non-negative");
+        }
+        if (mMasterPreference == 1 || mMasterPreference == 255 || mMasterPreference > 255) {
+            throw new IllegalArgumentException("Master Preference specification must not "
+                    + "exceed 255 or use 1 or 255 (reserved values)");
+        }
+        if (mClusterLow < CLUSTER_ID_MIN) {
+            throw new IllegalArgumentException("Cluster specification must be non-negative");
+        }
+        if (mClusterLow > CLUSTER_ID_MAX) {
+            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+        }
+        if (mClusterHigh < CLUSTER_ID_MIN) {
+            throw new IllegalArgumentException("Cluster specification must be non-negative");
+        }
+        if (mClusterHigh > CLUSTER_ID_MAX) {
+            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+        }
+        if (mClusterLow > mClusterHigh) {
+            throw new IllegalArgumentException(
+                    "Invalid argument combination - must have Cluster Low <= Cluster High");
+        }
+    }
+
+    /**
      * Builder used to build {@link ConfigRequest} objects.
      */
     public static final class Builder {
-        private boolean mSupport5gBand;
-        private int mMasterPreference;
-        private int mClusterLow;
-        private int mClusterHigh;
+        private boolean mSupport5gBand = false;
+        private int mMasterPreference = 0;
+        private int mClusterLow = CLUSTER_ID_MIN;
+        private int mClusterHigh = CLUSTER_ID_MAX;
+        private boolean mEnableIdentityChangeCallback = false;
 
         /**
-         * Default constructor for the Builder.
-         */
-        public Builder() {
-            mSupport5gBand = false;
-            mMasterPreference = 0;
-            mClusterLow = 0;
-            mClusterHigh = CLUSTER_ID_MAX;
-        }
-
-        /**
-         * Specify whether 5G band support is required in this request.
+         * Specify whether 5G band support is required in this request. Disabled by default.
          *
          * @param support5gBand Support for 5G band is required.
+         *
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setSupport5gBand(boolean support5gBand) {
             mSupport5gBand = support5gBand;
@@ -177,12 +257,15 @@
         }
 
         /**
-         * Specify the Master Preference requested. The permitted range is 0 to
+         * Specify the Master Preference requested. The permitted range is 0 (the default) to
          * 255 with 1 and 255 excluded (reserved).
          *
          * @param masterPreference The requested master preference
+         *
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setMasterPreference(int masterPreference) {
             if (masterPreference < 0) {
@@ -202,13 +285,16 @@
          * The Cluster ID is generated randomly for new NAN networks. Specify
          * the lower range of the cluster ID. The upper range is specified using
          * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
-         * range is 0 to the value specified by
-         * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality is
+         * range is 0 (the default) to the value specified by
+         * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality of Low and High is
          * permitted which restricts the Cluster ID to the specified value.
          *
          * @param clusterLow The lower range of the generated cluster ID.
+         *
          * @return The builder to facilitate chaining
          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         *
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setClusterLow(int clusterLow) {
             if (clusterLow < CLUSTER_ID_MIN) {
@@ -227,12 +313,15 @@
          * the lower upper of the cluster ID. The lower range is specified using
          * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
          * range is the value specified by
-         * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF. Equality
-         * is permitted which restricts the Cluster ID to the specified value.
+         * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF (the default). Equality of
+         * Low and High is permitted which restricts the Cluster ID to the specified value.
          *
          * @param clusterHigh The upper range of the generated cluster ID.
+         *
          * @return The builder to facilitate chaining
          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         *
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setClusterHigh(int clusterHigh) {
             if (clusterHigh < CLUSTER_ID_MIN) {
@@ -247,6 +336,27 @@
         }
 
         /**
+         * Indicate whether or not we want to enable the
+         * {@link WifiNanEventCallback#onIdentityChanged(byte[])} callback. A
+         * device identity is its Discovery MAC address which is randomized at regular intervals.
+         * An application may need to know the MAC address, e.g. when using OOB (out-of-band)
+         * discovery together with NAN connections.
+         * <p>
+         *     The callbacks are disabled by default since it may result in additional wake-ups
+         *     of the host -
+         *     increasing power.
+         *
+         * @param enableIdentityChangeCallback Enable the callback informing
+         *            listener when identity is changed.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setEnableIdentityChangeCallback(boolean enableIdentityChangeCallback) {
+            mEnableIdentityChangeCallback = enableIdentityChangeCallback;
+            return this;
+        }
+
+        /**
          * Build {@link ConfigRequest} given the current requests made on the
          * builder.
          */
@@ -256,7 +366,8 @@
                         "Invalid argument combination - must have Cluster Low <= Cluster High");
             }
 
-            return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh);
+            return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh,
+                    mEnableIdentityChangeCallback);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
similarity index 66%
rename from wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
rename to wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
index fa666af..a4e590b 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
@@ -17,16 +17,20 @@
 package android.net.wifi.nan;
 
 import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.RttManager;
 
 /**
  * Callback interface that WifiNanManager implements
  *
  * {@hide}
  */
-oneway interface IWifiNanEventListener
+oneway interface IWifiNanEventCallback
 {
-    void onConfigCompleted(in ConfigRequest completedConfig);
-    void onConfigFailed(in ConfigRequest failedConfig, int reason);
-    void onNanDown(int reason);
-    void onIdentityChanged();
+    void onConnectSuccess();
+    void onConnectFail(int reason);
+    void onIdentityChanged(in byte[] mac);
+
+    void onRangingSuccess(int rangingId, in RttManager.ParcelableRttResults results);
+    void onRangingFailure(int rangingId, int reason, in String description);
+    void onRangingAborted(int rangingId);
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index f382d97..17ec1bc 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -19,12 +19,11 @@
 import android.app.PendingIntent;
 
 import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.RttManager;
 
 /**
  * Interface that WifiNanService implements
@@ -33,18 +32,25 @@
  */
 interface IWifiNanManager
 {
+    // NAN API
+    void enableUsage();
+    void disableUsage();
+    boolean isUsageEnabled();
+
     // client API
-    void connect(in IBinder binder, in IWifiNanEventListener listener, int events);
-    void disconnect(in IBinder binder);
-    void requestConfig(in ConfigRequest configRequest);
+    int connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback,
+            in ConfigRequest configRequest);
+    void disconnect(int clientId, in IBinder binder);
+
+    void publish(int clientId, in PublishConfig publishConfig, in IWifiNanSessionCallback callback);
+    void subscribe(int clientId, in SubscribeConfig subscribeConfig,
+            in IWifiNanSessionCallback callback);
 
     // session API
-    int createSession(in IWifiNanSessionListener listener, int events);
-    void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings);
-    void subscribe(int sessionId, in SubscribeData subscribeData,
-            in SubscribeSettings subscribeSettings);
-    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength,
-            int messageId);
-    void stopSession(int sessionId);
-    void destroySession(int sessionId);
+    void updatePublish(int clientId, int sessionId, in PublishConfig publishConfig);
+    void updateSubscribe(int clientId, int sessionId, in SubscribeConfig subscribeConfig);
+    void sendMessage(int clientId, int sessionId, int peerId, in byte[] message, int messageId,
+        int retryCount);
+    void terminateSession(int clientId, int sessionId);
+    int startRanging(int clientId, int sessionId, in RttManager.ParcelableRttParams parms);
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
similarity index 65%
rename from wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
rename to wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
index d60d8ca..ff2c409 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
@@ -21,18 +21,16 @@
  *
  * {@hide}
  */
-oneway interface IWifiNanSessionListener
+oneway interface IWifiNanSessionCallback
 {
-    void onPublishFail(int reason);
-    void onPublishTerminated(int reason);
+    void onSessionStarted(int sessionId);
+    void onSessionConfigSuccess();
+    void onSessionConfigFail(int reason);
+    void onSessionTerminated(int reason);
 
-    void onSubscribeFail(int reason);
-    void onSubscribeTerminated(int reason);
-
-    void onMatch(int peerId, in byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
+    void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
 
     void onMessageSendSuccess(int messageId);
     void onMessageSendFail(int messageId, int reason);
-    void onMessageReceived(int peerId, in byte[] message, int messageLength);
+    void onMessageReceived(int peerId, in byte[] message);
 }
diff --git a/wifi/java/android/net/wifi/nan/LvBufferUtils.java b/wifi/java/android/net/wifi/nan/LvBufferUtils.java
new file mode 100644
index 0000000..eb56070
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/LvBufferUtils.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+import android.annotation.Nullable;
+
+import libcore.io.Memory;
+
+import java.nio.ByteOrder;
+import java.util.Iterator;
+
+/**
+ * Utility class to construct and parse byte arrays using the LV format -
+ * Length/Value format. The utilities accept a configuration of the size of
+ * the Length field.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class LvBufferUtils {
+    private LvBufferUtils() {
+        // no reason to ever create this class
+    }
+
+    /**
+     * Utility class to construct byte arrays using the LV format - Length/Value.
+     * <p>
+     * A constructor is created specifying the size of the Length (L) field.
+     * <p>
+     * The byte array is either provided (using
+     * {@link LvBufferUtils.LvConstructor#wrap(byte[])}) or allocated (using
+     * {@link LvBufferUtils.LvConstructor#allocate(int)}).
+     * <p>
+     * Values are added to the structure using the {@code LvConstructor.put*()}
+     * methods.
+     * <p>
+     * The final byte array is obtained using {@link LvBufferUtils.LvConstructor#getArray()}.
+     */
+    public static class LvConstructor {
+        private TlvBufferUtils.TlvConstructor mTlvImpl;
+
+        /**
+         * Define a LV constructor with the specified size of the Length (L) field.
+         *
+         * @param lengthSize Number of bytes used for the Length (L) field.
+         *            Values of 1 or 2 bytes are allowed.
+         */
+        public LvConstructor(int lengthSize) {
+            mTlvImpl = new TlvBufferUtils.TlvConstructor(0, lengthSize);
+        }
+
+        /**
+         * Set the byte array to be used to construct the LV.
+         *
+         * @param array Byte array to be formatted.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor wrap(@Nullable byte[] array) {
+            mTlvImpl.wrap(array);
+            return this;
+        }
+
+        /**
+         * Allocates a new byte array to be used ot construct a LV.
+         *
+         * @param capacity The size of the byte array to be allocated.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor allocate(int capacity) {
+            mTlvImpl.allocate(capacity);
+            return this;
+        }
+
+        /**
+         * Copies a byte into the LV array.
+         *
+         * @param b The byte to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putByte(byte b) {
+            mTlvImpl.putByte(0, b);
+            return this;
+        }
+
+        /**
+         * Copies a byte array into the LV.
+         *
+         * @param array The array to be copied into the LV structure.
+         * @param offset Start copying from the array at the specified offset.
+         * @param length Copy the specified number (length) of bytes from the
+         *            array.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putByteArray(@Nullable byte[] array, int offset,
+                int length) {
+            mTlvImpl.putByteArray(0, array, offset, length);
+            return this;
+        }
+
+        /**
+         * Copies a byte array into the LV.
+         *
+         * @param array The array to be copied (in full) into the LV structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putByteArray(int type, @Nullable byte[] array) {
+            return putByteArray(array, 0, (array == null) ? 0 : array.length);
+        }
+
+        /**
+         * Places a zero length element (i.e. Length field = 0) into the LV.
+         *
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putZeroLengthElement() {
+            mTlvImpl.putZeroLengthElement(0);
+            return this;
+        }
+
+        /**
+         * Copies short into the LV.
+         *
+         * @param data The short to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putShort(short data) {
+            mTlvImpl.putShort(0, data);
+            return this;
+        }
+
+        /**
+         * Copies integer into the LV.
+         *
+         * @param data The integer to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putInt(int data) {
+            mTlvImpl.putInt(0, data);
+            return this;
+        }
+
+        /**
+         * Copies a String's byte representation into the LV.
+         *
+         * @param data The string whose bytes are to be inserted into the
+         *            structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public LvBufferUtils.LvConstructor putString(@Nullable String data) {
+            mTlvImpl.putString(0, data);
+            return this;
+        }
+
+        /**
+         * Returns the constructed LV formatted byte-array. This array is a copy of the wrapped
+         * or allocated array - truncated to just the significant bytes - i.e. those written into
+         * the LV.
+         *
+         * @return The byte array containing the LV formatted structure.
+         */
+        public byte[] getArray() {
+            return mTlvImpl.getArray();
+        }
+    }
+
+    /**
+     * Utility class used when iterating over an LV formatted byte-array. Use
+     * {@link LvBufferUtils.LvIterable} to iterate over array. A {@link LvBufferUtils.LvElement}
+     * represents each entry in a LV formatted byte-array.
+     */
+    public static class LvElement {
+        /**
+         * The Length (L) field of the current LV element.
+         */
+        public int length;
+
+        /**
+         * The Value (V) field - a raw byte array representing the current LV
+         * element where the entry starts at {@link LvBufferUtils.LvElement#offset}.
+         */
+        public byte[] refArray;
+
+        /**
+         * The offset to be used into {@link LvBufferUtils.LvElement#refArray} to access the
+         * raw data representing the current LV element.
+         */
+        public int offset;
+
+        private LvElement(int length, @Nullable byte[] refArray, int offset) {
+            this.length = length;
+            this.refArray = refArray;
+            this.offset = offset;
+        }
+
+        /**
+         * Utility function to return a byte representation of a LV element of
+         * length 1. Note: an attempt to call this function on a LV item whose
+         * {@link LvBufferUtils.LvElement#length} is != 1 will result in an exception.
+         *
+         * @return byte representation of current LV element.
+         */
+        public byte getByte() {
+            if (length != 1) {
+                throw new IllegalArgumentException(
+                        "Accesing a byte from a LV element of length " + length);
+            }
+            return refArray[offset];
+        }
+
+        /**
+         * Utility function to return a short representation of a LV element of
+         * length 2. Note: an attempt to call this function on a LV item whose
+         * {@link LvBufferUtils.LvElement#length} is != 2 will result in an exception.
+         *
+         * @return short representation of current LV element.
+         */
+        public short getShort() {
+            if (length != 2) {
+                throw new IllegalArgumentException(
+                        "Accesing a short from a LV element of length " + length);
+            }
+            return Memory.peekShort(refArray, offset, ByteOrder.BIG_ENDIAN);
+        }
+
+        /**
+         * Utility function to return an integer representation of a LV element
+         * of length 4. Note: an attempt to call this function on a LV item
+         * whose {@link LvBufferUtils.LvElement#length} is != 4 will result in an exception.
+         *
+         * @return integer representation of current LV element.
+         */
+        public int getInt() {
+            if (length != 4) {
+                throw new IllegalArgumentException(
+                        "Accesing an int from a LV element of length " + length);
+            }
+            return Memory.peekInt(refArray, offset, ByteOrder.BIG_ENDIAN);
+        }
+
+        /**
+         * Utility function to return a String representation of a LV element.
+         *
+         * @return String representation of the current LV element.
+         */
+        public String getString() {
+            return new String(refArray, offset, length);
+        }
+    }
+
+    /**
+     * Utility class to iterate over a LV formatted byte-array.
+     */
+    public static class LvIterable implements Iterable<LvBufferUtils.LvElement> {
+        private final TlvBufferUtils.TlvIterable mTlvIterable;
+
+        /**
+         * Constructs an LvIterable object - specifying the format of the LV
+         * (the size of the Length field), and the byte array whose data is to be parsed.
+         *
+         * @param lengthSize Number of bytes sued for the Length (L) field.
+         *            Values values are 1 or 2 bytes.
+         * @param array The LV formatted byte-array to parse.
+         */
+        public LvIterable(int lengthSize, @Nullable byte[] array) {
+            mTlvIterable = new TlvBufferUtils.TlvIterable(0, lengthSize, array);
+        }
+
+        /**
+         * Prints out a parsed representation of the LV-formatted byte array.
+         * Whenever possible bytes, shorts, and integer are printed out (for
+         * fields whose length is 1, 2, or 4 respectively).
+         */
+        @Override
+        public String toString() {
+            return mTlvIterable.toString();
+        }
+
+        /**
+         * Returns an iterator to step through a LV formatted byte-array. The
+         * individual elements returned by the iterator are {@link LvBufferUtils.LvElement}.
+         */
+        @Override
+        public Iterator<LvBufferUtils.LvElement> iterator() {
+            return new Iterator<LvBufferUtils.LvElement>() {
+                private Iterator<TlvBufferUtils.TlvElement> mTlvIterator = mTlvIterable.iterator();
+
+                @Override
+                public boolean hasNext() {
+                    return mTlvIterator.hasNext();
+                }
+
+                @Override
+                public LvBufferUtils.LvElement next() {
+                    TlvBufferUtils.TlvElement tlvE = mTlvIterator.next();
+
+                    return new LvElement(tlvE.length, tlvE.refArray, tlvE.offset);
+                }
+
+                @Override
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+    }
+
+    /**
+     * Validates that a LV array is constructed correctly. I.e. that its specified Length
+     * fields correctly fill the specified length (and do not overshoot).
+     *
+     * @param array The LV array to verify.
+     * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
+     * @return A boolean indicating whether the array is valid (true) or invalid (false).
+     */
+    public static boolean isValid(@Nullable byte[] array, int lengthSize) {
+        return TlvBufferUtils.isValid(array, 0, lengthSize);
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl b/wifi/java/android/net/wifi/nan/PublishConfig.aidl
similarity index 95%
copy from wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
copy to wifi/java/android/net/wifi/nan/PublishConfig.aidl
index 44849bc..5f66d16 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
+++ b/wifi/java/android/net/wifi/nan/PublishConfig.aidl
@@ -16,4 +16,4 @@
 
 package android.net.wifi.nan;
 
-parcelable SubscribeSettings;
+parcelable PublishConfig;
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java
new file mode 100644
index 0000000..71f99d9
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishConfig.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import libcore.util.HexEncoding;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Defines the configuration of a NAN publish session. Built using
+ * {@link PublishConfig.Builder}. A publish session is created using
+ * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or updated using
+ * {@link WifiNanPublishSession#updatePublish(PublishConfig)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class PublishConfig implements Parcelable {
+    /** @hide */
+    @IntDef({
+            PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PublishTypes {
+    }
+
+    /**
+     * Defines an unsolicited publish session - a publish session where the publisher is
+     * advertising itself by broadcasting on-the-air. An unsolicited publish session is paired
+     * with an passive subscribe session {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE}.
+     * Configuration is done using {@link PublishConfig.Builder#setPublishType(int)}.
+     */
+    public static final int PUBLISH_TYPE_UNSOLICITED = 0;
+
+    /**
+     * Defines a solicited publish session - a publish session which is silent, waiting for a
+     * matching active subscribe session - and responding to it in unicast. A
+     * solicited publish session is paired with an active subscribe session
+     * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE}. Configuration is done using
+     * {@link PublishConfig.Builder#setPublishType(int)}.
+     */
+    public static final int PUBLISH_TYPE_SOLICITED = 1;
+
+    /** @hide */
+    public final byte[] mServiceName;
+
+    /** @hide */
+    public final byte[] mServiceSpecificInfo;
+
+    /** @hide */
+    public final byte[] mMatchFilter;
+
+    /** @hide */
+    public final int mPublishType;
+
+    /** @hide */
+    public final int mPublishCount;
+
+    /** @hide */
+    public final int mTtlSec;
+
+    /** @hide */
+    public final boolean mEnableTerminateNotification;
+
+    private PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+            int publishType, int publichCount, int ttlSec, boolean enableTerminateNotification) {
+        mServiceName = serviceName;
+        mServiceSpecificInfo = serviceSpecificInfo;
+        mMatchFilter = matchFilter;
+        mPublishType = publishType;
+        mPublishCount = publichCount;
+        mTtlSec = ttlSec;
+        mEnableTerminateNotification = enableTerminateNotification;
+    }
+
+    @Override
+    public String toString() {
+        return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
+                (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+                + ", mTxFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString()
+                + ", mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
+                + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
+                + mEnableTerminateNotification + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mServiceName);
+        dest.writeByteArray(mServiceSpecificInfo);
+        dest.writeByteArray(mMatchFilter);
+        dest.writeInt(mPublishType);
+        dest.writeInt(mPublishCount);
+        dest.writeInt(mTtlSec);
+        dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+    }
+
+    public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
+        @Override
+        public PublishConfig[] newArray(int size) {
+            return new PublishConfig[size];
+        }
+
+        @Override
+        public PublishConfig createFromParcel(Parcel in) {
+            byte[] serviceName = in.createByteArray();
+            byte[] ssi = in.createByteArray();
+            byte[] matchFilter = in.createByteArray();
+            int publishType = in.readInt();
+            int publishCount = in.readInt();
+            int ttlSec = in.readInt();
+            boolean enableTerminateNotification = in.readInt() != 0;
+
+            return new PublishConfig(serviceName, ssi, matchFilter, publishType, publishCount,
+                    ttlSec, enableTerminateNotification);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PublishConfig)) {
+            return false;
+        }
+
+        PublishConfig lhs = (PublishConfig) o;
+
+        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
+                lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
+                && mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
+                && mTtlSec == lhs.mTtlSec
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + Arrays.hashCode(mServiceName);
+        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+        result = 31 * result + Arrays.hashCode(mMatchFilter);
+        result = 31 * result + mPublishType;
+        result = 31 * result + mPublishCount;
+        result = 31 * result + mTtlSec;
+        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+
+        return result;
+    }
+
+    /**
+     * Verifies that the contents of the PublishConfig are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        WifiNanUtils.validateServiceName(mServiceName);
+
+        if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+            throw new IllegalArgumentException(
+                    "Invalid txFilter configuration - LV fields do not match up to length");
+        }
+        if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) {
+            throw new IllegalArgumentException("Invalid publishType - " + mPublishType);
+        }
+        if (mPublishCount < 0) {
+            throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
+        }
+        if (mTtlSec < 0) {
+            throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+        }
+    }
+
+    /**
+     * Builder used to build {@link PublishConfig} objects.
+     */
+    public static final class Builder {
+        private byte[] mServiceName;
+        private byte[] mServiceSpecificInfo;
+        private byte[] mMatchFilter;
+        private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
+        private int mPublishCount = 0;
+        private int mTtlSec = 0;
+        private boolean mEnableTerminateNotification = true;
+
+        /**
+         * Specify the service name of the publish session. The actual on-air
+         * value is a 6 byte hashed representation of this string.
+         * <p>
+         * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length.
+         * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric
+         * values (A-Z, a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte
+         * UTF-8 characters are acceptable in a Service Name.
+         * <p>
+         * Must be called - an empty ServiceName is not valid.
+         *
+         * @param serviceName The service name for the publish session.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceName(@NonNull String serviceName) {
+            if (serviceName == null) {
+                throw new IllegalArgumentException("Invalid service name - must be non-null");
+            }
+            mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the publish session. This is
+         * a free-form byte array available to the application to send
+         * additional information as part of the discovery operation - it
+         * will not be used to determine whether a publish/subscribe match
+         * occurs.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param serviceSpecificInfo A byte-array for the service-specific
+         *            information field.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
+            mServiceSpecificInfo = serviceSpecificInfo;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the publish session - a simple wrapper
+         * of {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}
+         * obtaining the data from a String.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param serviceSpecificInfoStr The service specific information string
+         *            to be included (as a byte array) in the publish
+         *            information.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(@NonNull String serviceSpecificInfoStr) {
+            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+            return this;
+        }
+
+        /**
+         * The match filter for a publish session. Used to determine whether a service
+         * discovery occurred - in addition to relying on the service name.
+         * <p>
+         * Format is an LV byte array: a single byte Length field followed by L bytes (the value of
+         * the Length field) of a value blob.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param matchFilter The byte-array containing the LV formatted match filter.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setMatchFilter(@Nullable byte[] matchFilter) {
+            mMatchFilter = matchFilter;
+            return this;
+        }
+
+        /**
+         * Specify the type of the publish session: solicited (aka active - publish
+         * packets are transmitted over-the-air), or unsolicited (aka passive -
+         * no publish packets are transmitted, a match is made against an active
+         * subscribe session whose packets are transmitted over-the-air).
+         *
+         * @param publishType Publish session type:
+         *            {@link PublishConfig#PUBLISH_TYPE_SOLICITED} or
+         *            {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED} (the default).
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setPublishType(@PublishTypes int publishType) {
+            if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
+                throw new IllegalArgumentException("Invalid publishType - " + publishType);
+            }
+            mPublishType = publishType;
+            return this;
+        }
+
+        /**
+         * Sets the number of times an unsolicited (configured using
+         * {@link PublishConfig.Builder#setPublishType(int)}) publish session
+         * will be broadcast. When the count is reached an event will be
+         * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)}
+         * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE} [unless
+         * {@link #setEnableTerminateNotification(boolean)} disables the callback].
+         * <p>
+         *     Optional. 0 by default - indicating the session doesn't terminate on its own.
+         *     Session will be terminated when {@link WifiNanSession#terminate()} is called.
+         *
+         * @param publishCount Number of publish packets to broadcast.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setPublishCount(int publishCount) {
+            if (publishCount < 0) {
+                throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
+            }
+            mPublishCount = publishCount;
+            return this;
+        }
+
+        /**
+         * Sets the time interval (in seconds) an unsolicited (
+         * {@link PublishConfig.Builder#setPublishType(int)}) publish session
+         * will be alive - broadcasting a packet. When the TTL is reached
+         * an event will be generated for
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} with
+         * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}  [unless
+         * {@link #setEnableTerminateNotification(boolean)} disables the callback].
+         * <p>
+         *     Optional. 0 by default - indicating the session doesn't terminate on its own.
+         *     Session will be terminated when {@link WifiNanSession#terminate()} is called.
+         *
+         * @param ttlSec Lifetime of a publish session in seconds.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTtlSec(int ttlSec) {
+            if (ttlSec < 0) {
+                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+            }
+            mTtlSec = ttlSec;
+            return this;
+        }
+
+        /**
+         * Configure whether a publish terminate notification
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported
+         * back to the callback.
+         *
+         * @param enable If true the terminate callback will be called when the
+         *            publish is terminated. Otherwise it will not be called.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setEnableTerminateNotification(boolean enable) {
+            mEnableTerminateNotification = enable;
+            return this;
+        }
+
+        /**
+         * Build {@link PublishConfig} given the current requests made on the
+         * builder.
+         */
+        public PublishConfig build() {
+            return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
+                    mPublishCount, mTtlSec, mEnableTerminateNotification);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishData.aidl b/wifi/java/android/net/wifi/nan/PublishData.aidl
deleted file mode 100644
index 15e4ddf..0000000
--- a/wifi/java/android/net/wifi/nan/PublishData.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-parcelable PublishData;
diff --git a/wifi/java/android/net/wifi/nan/PublishData.java b/wifi/java/android/net/wifi/nan/PublishData.java
deleted file mode 100644
index 80119eb..0000000
--- a/wifi/java/android/net/wifi/nan/PublishData.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * Defines the data for a NAN publish session. Built using
- * {@link PublishData.Builder}. Publish is done using
- * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
- * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
- * @hide PROPOSED_NAN_API
- */
-public class PublishData implements Parcelable {
-    /**
-     * @hide
-     */
-    public final String mServiceName;
-
-    /**
-     * @hide
-     */
-    public final int mServiceSpecificInfoLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mServiceSpecificInfo;
-
-    /**
-     * @hide
-     */
-    public final int mTxFilterLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mTxFilter;
-
-    /**
-     * @hide
-     */
-    public final int mRxFilterLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mRxFilter;
-
-    private PublishData(String serviceName, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
-            int rxFilterLength) {
-        mServiceName = serviceName;
-        mServiceSpecificInfoLength = serviceSpecificInfoLength;
-        mServiceSpecificInfo = serviceSpecificInfo;
-        mTxFilterLength = txFilterLength;
-        mTxFilter = txFilter;
-        mRxFilterLength = rxFilterLength;
-        mRxFilter = rxFilter;
-    }
-
-    @Override
-    public String toString() {
-        return "PublishData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
-                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
-                + "', mTxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
-                + ", mRxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
-                + "']";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mServiceName);
-        dest.writeInt(mServiceSpecificInfoLength);
-        if (mServiceSpecificInfoLength != 0) {
-            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
-        }
-        dest.writeInt(mTxFilterLength);
-        if (mTxFilterLength != 0) {
-            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
-        }
-        dest.writeInt(mRxFilterLength);
-        if (mRxFilterLength != 0) {
-            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
-        }
-    }
-
-    public static final Creator<PublishData> CREATOR = new Creator<PublishData>() {
-        @Override
-        public PublishData[] newArray(int size) {
-            return new PublishData[size];
-        }
-
-        @Override
-        public PublishData createFromParcel(Parcel in) {
-            String serviceName = in.readString();
-            int ssiLength = in.readInt();
-            byte[] ssi = new byte[ssiLength];
-            if (ssiLength != 0) {
-                in.readByteArray(ssi);
-            }
-            int txFilterLength = in.readInt();
-            byte[] txFilter = new byte[txFilterLength];
-            if (txFilterLength != 0) {
-                in.readByteArray(txFilter);
-            }
-            int rxFilterLength = in.readInt();
-            byte[] rxFilter = new byte[rxFilterLength];
-            if (rxFilterLength != 0) {
-                in.readByteArray(rxFilter);
-            }
-
-            return new PublishData(serviceName, ssi, ssiLength, txFilter, txFilterLength, rxFilter,
-                    rxFilterLength);
-        }
-    };
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof PublishData)) {
-            return false;
-        }
-
-        PublishData lhs = (PublishData) o;
-
-        if (!mServiceName.equals(lhs.mServiceName)
-                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
-                || mTxFilterLength != lhs.mTxFilterLength
-                || mRxFilterLength != lhs.mRxFilterLength) {
-            return false;
-        }
-
-        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
-            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
-                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
-                    return false;
-                }
-            }
-        } else if (mServiceSpecificInfoLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mTxFilter != null && lhs.mTxFilter != null) {
-            for (int i = 0; i < mTxFilterLength; ++i) {
-                if (mTxFilter[i] != lhs.mTxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mTxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mRxFilter != null && lhs.mRxFilter != null) {
-            for (int i = 0; i < mRxFilterLength; ++i) {
-                if (mRxFilter[i] != lhs.mRxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mRxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + mServiceName.hashCode();
-        result = 31 * result + mServiceSpecificInfoLength;
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + mTxFilterLength;
-        result = 31 * result + Arrays.hashCode(mTxFilter);
-        result = 31 * result + mRxFilterLength;
-        result = 31 * result + Arrays.hashCode(mRxFilter);
-
-        return result;
-    }
-
-    /**
-     * Builder used to build {@link PublishData} objects.
-     */
-    public static final class Builder {
-        private String mServiceName;
-        private int mServiceSpecificInfoLength;
-        private byte[] mServiceSpecificInfo = new byte[0];
-        private int mTxFilterLength;
-        private byte[] mTxFilter = new byte[0];
-        private int mRxFilterLength;
-        private byte[] mRxFilter = new byte[0];
-
-        /**
-         * Specify the service name of the publish session. The actual on-air
-         * value is a 6 byte hashed representation of this string.
-         *
-         * @param serviceName The service name for the publish session.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceName(String serviceName) {
-            mServiceName = serviceName;
-            return this;
-        }
-
-        /**
-         * Specify service specific information for the publish session. This is
-         * a free-form byte array available to the application to send
-         * additional information as part of the discovery operation - i.e. it
-         * will not be used to determine whether a publish/subscribe match
-         * occurs.
-         *
-         * @param serviceSpecificInfo A byte-array for the service-specific
-         *            information field.
-         * @param serviceSpecificInfoLength The length of the byte-array to be
-         *            used.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength) {
-            if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null
-                    || serviceSpecificInfo.length < serviceSpecificInfoLength)) {
-                throw new IllegalArgumentException("Non-matching combination of "
-                        + "serviceSpecificInfo and serviceSpecificInfoLength");
-            }
-            mServiceSpecificInfoLength = serviceSpecificInfoLength;
-            mServiceSpecificInfo = serviceSpecificInfo;
-            return this;
-        }
-
-        /**
-         * Specify service specific information for the publish session - same
-         * as {@link PublishData.Builder#setServiceSpecificInfo(byte[], int)}
-         * but obtaining the data from a String.
-         *
-         * @param serviceSpecificInfoStr The service specific information string
-         *            to be included (as a byte array) in the publish
-         *            information.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
-            mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
-            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
-            return this;
-        }
-
-        /**
-         * The transmit filter for an active publish session
-         * {@link PublishSettings.Builder#setPublishType(int)} and
-         * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}. Included in
-         * transmitted publish packets and used by receivers (subscribers) to
-         * determine whether they match - in addition to just relying on the
-         * service name.
-         * <p>
-         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
-         * is available to form and parse.
-         *
-         * @param txFilter The byte-array containing the LV formatted transmit
-         *            filter.
-         * @param txFilterLength The number of bytes in the transmit filter
-         *            argument.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
-            if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of txFilter and txFilterLength");
-            }
-            mTxFilter = txFilter;
-            mTxFilterLength = txFilterLength;
-            return this;
-        }
-
-        /**
-         * The transmit filter for a passive publish session
-         * {@link PublishSettings.Builder#setPublishType(int)} and
-         * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}. Used by the publisher
-         * to determine whether they match transmitted subscriber packets
-         * (active subscribers) - in addition to just relying on the service
-         * name.
-         * <p>
-         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
-         * is available to form and parse.
-         *
-         * @param rxFilter The byte-array containing the LV formatted receive
-         *            filter.
-         * @param rxFilterLength The number of bytes in the receive filter
-         *            argument.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
-            if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of rxFilter and rxFilterLength");
-            }
-            mRxFilter = rxFilter;
-            mRxFilterLength = rxFilterLength;
-            return this;
-        }
-
-        /**
-         * Build {@link PublishData} given the current requests made on the
-         * builder.
-         */
-        public PublishData build() {
-            return new PublishData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
-                    mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
-        }
-    }
-}
diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.aidl b/wifi/java/android/net/wifi/nan/PublishSettings.aidl
deleted file mode 100644
index ff69293..0000000
--- a/wifi/java/android/net/wifi/nan/PublishSettings.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-parcelable PublishSettings;
diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.java b/wifi/java/android/net/wifi/nan/PublishSettings.java
deleted file mode 100644
index bbc5340..0000000
--- a/wifi/java/android/net/wifi/nan/PublishSettings.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Defines the settings (configuration) for a NAN publish session. Built using
- * {@link PublishSettings.Builder}. Publish is done using
- * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
- * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
- *
- * @hide PROPOSED_NAN_API
- */
-public class PublishSettings implements Parcelable {
-
-    /**
-     * Defines an unsolicited publish session - i.e. a publish session where
-     * publish packets are transmitted over-the-air. Configuration is done using
-     * {@link PublishSettings.Builder#setPublishType(int)}.
-     */
-    public static final int PUBLISH_TYPE_UNSOLICITED = 0;
-
-    /**
-     * Defines a solicited publish session - i.e. a publish session where
-     * publish packets are not transmitted over-the-air and the device listens
-     * and matches to transmitted subscribe packets. Configuration is done using
-     * {@link PublishSettings.Builder#setPublishType(int)}.
-     */
-    public static final int PUBLISH_TYPE_SOLICITED = 1;
-
-    /**
-     * @hide
-     */
-    public final int mPublishType;
-
-    /**
-     * @hide
-     */
-    public final int mPublishCount;
-
-    /**
-     * @hide
-     */
-    public final int mTtlSec;
-
-    private PublishSettings(int publishType, int publichCount, int ttlSec) {
-        mPublishType = publishType;
-        mPublishCount = publichCount;
-        mTtlSec = ttlSec;
-    }
-
-    @Override
-    public String toString() {
-        return "PublishSettings [mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
-                + ", mTtlSec=" + mTtlSec + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mPublishType);
-        dest.writeInt(mPublishCount);
-        dest.writeInt(mTtlSec);
-    }
-
-    public static final Creator<PublishSettings> CREATOR = new Creator<PublishSettings>() {
-        @Override
-        public PublishSettings[] newArray(int size) {
-            return new PublishSettings[size];
-        }
-
-        @Override
-        public PublishSettings createFromParcel(Parcel in) {
-            int publishType = in.readInt();
-            int publishCount = in.readInt();
-            int ttlSec = in.readInt();
-            return new PublishSettings(publishType, publishCount, ttlSec);
-        }
-    };
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof PublishSettings)) {
-            return false;
-        }
-
-        PublishSettings lhs = (PublishSettings) o;
-
-        return mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
-                && mTtlSec == lhs.mTtlSec;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + mPublishType;
-        result = 31 * result + mPublishCount;
-        result = 31 * result + mTtlSec;
-
-        return result;
-    }
-
-    /**
-     * Builder used to build {@link PublishSettings} objects.
-     */
-    public static final class Builder {
-        int mPublishType;
-        int mPublishCount;
-        int mTtlSec;
-
-        /**
-         * Sets the type of the publish session: solicited (aka active - publish
-         * packets are transmitted over-the-air), or unsolicited (aka passive -
-         * no publish packets are transmitted, a match is made against an active
-         * subscribe session whose packets are transmitted over-the-air).
-         *
-         * @param publishType Publish session type: solicited (
-         *            {@link PublishSettings#PUBLISH_TYPE_SOLICITED}) or
-         *            unsolicited (
-         *            {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}).
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setPublishType(int publishType) {
-            if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
-                throw new IllegalArgumentException("Invalid publishType - " + publishType);
-            }
-            mPublishType = publishType;
-            return this;
-        }
-
-        /**
-         * Sets the number of times a solicited (
-         * {@link PublishSettings.Builder#setPublishType(int)}) publish session
-         * will transmit a packet. When the count is reached an event will be
-         * generated for {@link WifiNanSessionListener#onPublishTerminated(int)}
-         * with reason={@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
-         *
-         * @param publishCount Number of publish packets to transmit.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setPublishCount(int publishCount) {
-            if (publishCount < 0) {
-                throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
-            }
-            mPublishCount = publishCount;
-            return this;
-        }
-
-        /**
-         * Sets the time interval (in seconds) a solicited (
-         * {@link PublishSettings.Builder#setPublishCount(int)}) publish session
-         * will be alive - i.e. transmitting a packet. When the TTL is reached
-         * an event will be generated for
-         * {@link WifiNanSessionListener#onPublishTerminated(int)} with reason=
-         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
-         *
-         * @param ttlSec Lifetime of a publish session in seconds.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setTtlSec(int ttlSec) {
-            if (ttlSec < 0) {
-                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
-            }
-            mTtlSec = ttlSec;
-            return this;
-        }
-
-        /**
-         * Build {@link PublishSettings} given the current requests made on the
-         * builder.
-         */
-        public PublishSettings build() {
-            return new PublishSettings(mPublishType, mPublishCount, mTtlSec);
-        }
-    }
-}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl b/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
rename to wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
index 44849bc..92344a4 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
+++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
@@ -16,4 +16,4 @@
 
 package android.net.wifi.nan;
 
-parcelable SubscribeSettings;
+parcelable SubscribeConfig;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
new file mode 100644
index 0000000..7904875
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import libcore.util.HexEncoding;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Defines the configuration of a NAN subscribe session. Built using
+ * {@link SubscribeConfig.Builder}. Subscribe is done using
+ * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} or
+ * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class SubscribeConfig implements Parcelable {
+    /** @hide */
+    @IntDef({
+            SUBSCRIBE_TYPE_PASSIVE, SUBSCRIBE_TYPE_ACTIVE })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SubscribeTypes {
+    }
+
+    /**
+     * Defines a passive subscribe session - a subscribe session where
+     * subscribe packets are not transmitted over-the-air and the device listens
+     * and matches to transmitted publish packets. Configuration is done using
+     * {@link SubscribeConfig.Builder#setSubscribeType(int)}.
+     */
+    public static final int SUBSCRIBE_TYPE_PASSIVE = 0;
+
+    /**
+     * Defines an active subscribe session - a subscribe session where
+     * subscribe packets are transmitted over-the-air. Configuration is done
+     * using {@link SubscribeConfig.Builder#setSubscribeType(int)}.
+     */
+    public static final int SUBSCRIBE_TYPE_ACTIVE = 1;
+
+    /** @hide */
+    @IntDef({
+            MATCH_STYLE_FIRST_ONLY, MATCH_STYLE_ALL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MatchStyles {
+    }
+
+    /**
+     * Specifies that only the first match of a set of identical matches (same
+     * publish) will be reported to the subscriber. Configuration is done using
+     * {@link SubscribeConfig.Builder#setMatchStyle(int)}.
+     */
+    public static final int MATCH_STYLE_FIRST_ONLY = 0;
+
+    /**
+     * Specifies that all matches of a set of identical matches (same publish)
+     * will be reported to the subscriber. Configuration is done using
+     * {@link SubscribeConfig.Builder#setMatchStyle(int)}.
+     */
+    public static final int MATCH_STYLE_ALL = 1;
+
+    /** @hide */
+    public final byte[] mServiceName;
+
+    /** @hide */
+    public final byte[] mServiceSpecificInfo;
+
+    /** @hide */
+    public final byte[] mMatchFilter;
+
+    /** @hide */
+    public final int mSubscribeType;
+
+    /** @hide */
+    public final int mSubscribeCount;
+
+    /** @hide */
+    public final int mTtlSec;
+
+    /** @hide */
+    public final int mMatchStyle;
+
+    /** @hide */
+    public final boolean mEnableTerminateNotification;
+
+    private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+            int subscribeType, int publichCount, int ttlSec, int matchStyle,
+            boolean enableTerminateNotification) {
+        mServiceName = serviceName;
+        mServiceSpecificInfo = serviceSpecificInfo;
+        mMatchFilter = matchFilter;
+        mSubscribeType = subscribeType;
+        mSubscribeCount = publichCount;
+        mTtlSec = ttlSec;
+        mMatchStyle = matchStyle;
+        mEnableTerminateNotification = enableTerminateNotification;
+    }
+
+    @Override
+    public String toString() {
+        return "SubscribeConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
+                (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+                + ", mMatchFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString()
+                + ", mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + mSubscribeCount
+                + ", mTtlSec=" + mTtlSec + ", mMatchType=" + mMatchStyle
+                + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mServiceName);
+        dest.writeByteArray(mServiceSpecificInfo);
+        dest.writeByteArray(mMatchFilter);
+        dest.writeInt(mSubscribeType);
+        dest.writeInt(mSubscribeCount);
+        dest.writeInt(mTtlSec);
+        dest.writeInt(mMatchStyle);
+        dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+    }
+
+    public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
+        @Override
+        public SubscribeConfig[] newArray(int size) {
+            return new SubscribeConfig[size];
+        }
+
+        @Override
+        public SubscribeConfig createFromParcel(Parcel in) {
+            byte[] serviceName = in.createByteArray();
+            byte[] ssi = in.createByteArray();
+            byte[] matchFilter = in.createByteArray();
+            int subscribeType = in.readInt();
+            int subscribeCount = in.readInt();
+            int ttlSec = in.readInt();
+            int matchStyle = in.readInt();
+            boolean enableTerminateNotification = in.readInt() != 0;
+
+            return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, subscribeCount,
+                    ttlSec, matchStyle, enableTerminateNotification);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof SubscribeConfig)) {
+            return false;
+        }
+
+        SubscribeConfig lhs = (SubscribeConfig) o;
+
+        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
+                lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
+                && mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
+                && mTtlSec == lhs.mTtlSec && mMatchStyle == lhs.mMatchStyle
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + Arrays.hashCode(mServiceName);
+        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+        result = 31 * result + Arrays.hashCode(mMatchFilter);
+        result = 31 * result + mSubscribeType;
+        result = 31 * result + mSubscribeCount;
+        result = 31 * result + mTtlSec;
+        result = 31 * result + mMatchStyle;
+        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+
+        return result;
+    }
+
+    /**
+     * Verifies that the contents of the SubscribeConfig are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        WifiNanUtils.validateServiceName(mServiceName);
+
+        if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+            throw new IllegalArgumentException(
+                    "Invalid matchFilter configuration - LV fields do not match up to length");
+        }
+        if (mSubscribeType < SUBSCRIBE_TYPE_PASSIVE || mSubscribeType > SUBSCRIBE_TYPE_ACTIVE) {
+            throw new IllegalArgumentException("Invalid subscribeType - " + mSubscribeType);
+        }
+        if (mSubscribeCount < 0) {
+            throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
+        }
+        if (mTtlSec < 0) {
+            throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+        }
+        if (mMatchStyle != MATCH_STYLE_FIRST_ONLY && mMatchStyle != MATCH_STYLE_ALL) {
+            throw new IllegalArgumentException(
+                    "Invalid matchType - must be MATCH_FIRST_ONLY or MATCH_ALL");
+        }
+    }
+
+    /**
+     * Builder used to build {@link SubscribeConfig} objects.
+     */
+    public static final class Builder {
+        private byte[] mServiceName;
+        private byte[] mServiceSpecificInfo;
+        private byte[] mMatchFilter;
+        private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
+        private int mSubscribeCount = 0;
+        private int mTtlSec = 0;
+        private int mMatchStyle = MATCH_STYLE_ALL;
+        private boolean mEnableTerminateNotification = true;
+
+        /**
+         * Specify the service name of the subscribe session. The actual on-air
+         * value is a 6 byte hashed representation of this string.
+         * <p>
+         * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length.
+         * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric
+         * values (A-Z, a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte
+         * UTF-8 characters are acceptable in a Service Name.
+         * <p>
+         * Must be called - an empty ServiceName is not valid.
+         *
+         * @param serviceName The service name for the subscribe session.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceName(@NonNull String serviceName) {
+            if (serviceName == null) {
+                throw new IllegalArgumentException("Invalid service name - must be non-null");
+            }
+            mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the subscribe session. This is
+         * a free-form byte array available to the application to send
+         * additional information as part of the discovery operation - i.e. it
+         * will not be used to determine whether a publish/subscribe match
+         * occurs.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param serviceSpecificInfo A byte-array for the service-specific
+         *            information field.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
+            mServiceSpecificInfo = serviceSpecificInfo;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the subscribe session - a simple wrapper
+         * of {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}
+         * obtaining the data from a String.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param serviceSpecificInfoStr The service specific information string
+         *            to be included (as a byte array) in the subscribe
+         *            information.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(@NonNull String serviceSpecificInfoStr) {
+            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+            return this;
+        }
+
+        /**
+         * The match filter for a subscribe session. Used to determine whether a service
+         * discovery occurred - in addition to relying on the service name.
+         * <p>
+         * Format is an LV byte array: a single byte Length field followed by L bytes (the value of
+         * the Length field) of a value blob.
+         * <p>
+         *     Optional. Empty by default.
+         *
+         * @param matchFilter The byte-array containing the LV formatted match filter.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setMatchFilter(@Nullable byte[] matchFilter) {
+            mMatchFilter = matchFilter;
+            return this;
+        }
+
+        /**
+         * Sets the type of the subscribe session: active (subscribe packets are
+         * transmitted over-the-air), or passive (no subscribe packets are
+         * transmitted, a match is made against a solicited/active publish
+         * session whose packets are transmitted over-the-air).
+         *
+         * @param subscribeType Subscribe session type:
+         *            {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} or
+         *            {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE}.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setSubscribeType(@SubscribeTypes int subscribeType) {
+            if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) {
+                throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType);
+            }
+            mSubscribeType = subscribeType;
+            return this;
+        }
+
+        /**
+         * Sets the number of times an active (
+         * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
+         * will broadcast. When the count is reached an event will be
+         * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)}
+         * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
+         * <p>
+         *     Optional. 0 by default - indicating the session doesn't terminate on its own.
+         *     Session will be terminated when {@link WifiNanSession#terminate()} is called.
+         *
+         * @param subscribeCount Number of subscribe packets to broadcast.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setSubscribeCount(int subscribeCount) {
+            if (subscribeCount < 0) {
+                throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
+            }
+            mSubscribeCount = subscribeCount;
+            return this;
+        }
+
+        /**
+         * Sets the time interval (in seconds) an active (
+         * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
+         * will be alive - i.e. broadcasting a packet. When the TTL is reached
+         * an event will be generated for
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} with
+         * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
+         * <p>
+         *     Optional. 0 by default - indicating the session doesn't terminate on its own.
+         *     Session will be terminated when {@link WifiNanSession#terminate()} is called.
+         *
+         * @param ttlSec Lifetime of a subscribe session in seconds.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTtlSec(int ttlSec) {
+            if (ttlSec < 0) {
+                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+            }
+            mTtlSec = ttlSec;
+            return this;
+        }
+
+        /**
+         * Sets the match style of the subscription - how are matches from a
+         * single match session (corresponding to the same publish action on the
+         * peer) reported to the host (using the
+         * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])}
+         * ). The options are: only report the first match and ignore the rest
+         * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single
+         * match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default).
+         *
+         * @param matchStyle The reporting style for the discovery match.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setMatchStyle(@MatchStyles int matchStyle) {
+            if (matchStyle != MATCH_STYLE_FIRST_ONLY && matchStyle != MATCH_STYLE_ALL) {
+                throw new IllegalArgumentException(
+                        "Invalid matchType - must be MATCH_FIRST_ONLY or MATCH_ALL");
+            }
+            mMatchStyle = matchStyle;
+            return this;
+        }
+
+        /**
+         * Configure whether a subscribe terminate notification
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported
+         * back to the callback.
+         *
+         * @param enable If true the terminate callback will be called when the
+         *            subscribe is terminated. Otherwise it will not be called.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setEnableTerminateNotification(boolean enable) {
+            mEnableTerminateNotification = enable;
+            return this;
+        }
+
+        /**
+         * Build {@link SubscribeConfig} given the current requests made on the
+         * builder.
+         */
+        public SubscribeConfig build() {
+            return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
+                    mSubscribeType, mSubscribeCount, mTtlSec, mMatchStyle,
+                    mEnableTerminateNotification);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.aidl b/wifi/java/android/net/wifi/nan/SubscribeData.aidl
deleted file mode 100644
index 662fdb8..0000000
--- a/wifi/java/android/net/wifi/nan/SubscribeData.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-parcelable SubscribeData;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.java b/wifi/java/android/net/wifi/nan/SubscribeData.java
deleted file mode 100644
index cd6e918..0000000
--- a/wifi/java/android/net/wifi/nan/SubscribeData.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * Defines the data for a NAN subscribe session. Built using
- * {@link SubscribeData.Builder}. Subscribe is done using
- * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
- * or
- * {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
- * @hide PROPOSED_NAN_API
- */
-public class SubscribeData implements Parcelable {
-    /**
-     * @hide
-     */
-    public final String mServiceName;
-
-    /**
-     * @hide
-     */
-    public final int mServiceSpecificInfoLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mServiceSpecificInfo;
-
-    /**
-     * @hide
-     */
-    public final int mTxFilterLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mTxFilter;
-
-    /**
-     * @hide
-     */
-    public final int mRxFilterLength;
-
-    /**
-     * @hide
-     */
-    public final byte[] mRxFilter;
-
-    private SubscribeData(String serviceName, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
-            int rxFilterLength) {
-        mServiceName = serviceName;
-        mServiceSpecificInfoLength = serviceSpecificInfoLength;
-        mServiceSpecificInfo = serviceSpecificInfo;
-        mTxFilterLength = txFilterLength;
-        mTxFilter = txFilter;
-        mRxFilterLength = rxFilterLength;
-        mRxFilter = rxFilter;
-    }
-
-    @Override
-    public String toString() {
-        return "SubscribeData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
-                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
-                + "', mTxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
-                + ", mRxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
-                + "']";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mServiceName);
-        dest.writeInt(mServiceSpecificInfoLength);
-        if (mServiceSpecificInfoLength != 0) {
-            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
-        }
-        dest.writeInt(mTxFilterLength);
-        if (mTxFilterLength != 0) {
-            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
-        }
-        dest.writeInt(mRxFilterLength);
-        if (mRxFilterLength != 0) {
-            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
-        }
-    }
-
-    public static final Creator<SubscribeData> CREATOR = new Creator<SubscribeData>() {
-        @Override
-        public SubscribeData[] newArray(int size) {
-            return new SubscribeData[size];
-        }
-
-        @Override
-        public SubscribeData createFromParcel(Parcel in) {
-            String serviceName = in.readString();
-            int ssiLength = in.readInt();
-            byte[] ssi = new byte[ssiLength];
-            if (ssiLength != 0) {
-                in.readByteArray(ssi);
-            }
-            int txFilterLength = in.readInt();
-            byte[] txFilter = new byte[txFilterLength];
-            if (txFilterLength != 0) {
-                in.readByteArray(txFilter);
-            }
-            int rxFilterLength = in.readInt();
-            byte[] rxFilter = new byte[rxFilterLength];
-            if (rxFilterLength != 0) {
-                in.readByteArray(rxFilter);
-            }
-
-            return new SubscribeData(serviceName, ssi, ssiLength, txFilter, txFilterLength,
-                    rxFilter, rxFilterLength);
-        }
-    };
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof SubscribeData)) {
-            return false;
-        }
-
-        SubscribeData lhs = (SubscribeData) o;
-
-        if (!mServiceName.equals(lhs.mServiceName)
-                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
-                || mTxFilterLength != lhs.mTxFilterLength
-                || mRxFilterLength != lhs.mRxFilterLength) {
-            return false;
-        }
-
-        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
-            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
-                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
-                    return false;
-                }
-            }
-        } else if (mServiceSpecificInfoLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mTxFilter != null && lhs.mTxFilter != null) {
-            for (int i = 0; i < mTxFilterLength; ++i) {
-                if (mTxFilter[i] != lhs.mTxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mTxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mRxFilter != null && lhs.mRxFilter != null) {
-            for (int i = 0; i < mRxFilterLength; ++i) {
-                if (mRxFilter[i] != lhs.mRxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mRxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + mServiceName.hashCode();
-        result = 31 * result + mServiceSpecificInfoLength;
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + mTxFilterLength;
-        result = 31 * result + Arrays.hashCode(mTxFilter);
-        result = 31 * result + mRxFilterLength;
-        result = 31 * result + Arrays.hashCode(mRxFilter);
-
-        return result;
-    }
-
-    /**
-     * Builder used to build {@link SubscribeData} objects.
-     */
-    public static final class Builder {
-        private String mServiceName;
-        private int mServiceSpecificInfoLength;
-        private byte[] mServiceSpecificInfo = new byte[0];
-        private int mTxFilterLength;
-        private byte[] mTxFilter = new byte[0];
-        private int mRxFilterLength;
-        private byte[] mRxFilter = new byte[0];
-
-        /**
-         * Specify the service name of the subscribe session. The actual on-air
-         * value is a 6 byte hashed representation of this string.
-         *
-         * @param serviceName The service name for the subscribe session.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceName(String serviceName) {
-            mServiceName = serviceName;
-            return this;
-        }
-
-        /**
-         * Specify service specific information for the subscribe session. This
-         * is a free-form byte array available to the application to send
-         * additional information as part of the discovery operation - i.e. it
-         * will not be used to determine whether a publish/subscribe match
-         * occurs.
-         *
-         * @param serviceSpecificInfo A byte-array for the service-specific
-         *            information field.
-         * @param serviceSpecificInfoLength The length of the byte-array to be
-         *            used.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength) {
-            mServiceSpecificInfoLength = serviceSpecificInfoLength;
-            mServiceSpecificInfo = serviceSpecificInfo;
-            return this;
-        }
-
-        /**
-         * Specify service specific information for the subscribe session - same
-         * as {@link SubscribeData.Builder#setServiceSpecificInfo(byte[], int)}
-         * but obtaining the data from a String.
-         *
-         * @param serviceSpecificInfoStr The service specific information string
-         *            to be included (as a byte array) in the subscribe
-         *            information.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
-            mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
-            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
-            return this;
-        }
-
-        /**
-         * The transmit filter for an active subscribe session
-         * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
-         * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}. Included in
-         * transmitted subscribe packets and used by receivers (passive
-         * publishers) to determine whether they match - in addition to just
-         * relying on the service name.
-         * <p>
-         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
-         * is available to form and parse.
-         *
-         * @param txFilter The byte-array containing the LV formatted transmit
-         *            filter.
-         * @param txFilterLength The number of bytes in the transmit filter
-         *            argument.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
-            mTxFilter = txFilter;
-            mTxFilterLength = txFilterLength;
-            return this;
-        }
-
-        /**
-         * The transmit filter for a passive subsribe session
-         * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
-         * {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}. Used by the
-         * subscriber to determine whether they match transmitted publish
-         * packets - in addition to just relying on the service name.
-         * <p>
-         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
-         * is available to form and parse.
-         *
-         * @param rxFilter The byte-array containing the LV formatted receive
-         *            filter.
-         * @param rxFilterLength The number of bytes in the receive filter
-         *            argument.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
-            mRxFilter = rxFilter;
-            mRxFilterLength = rxFilterLength;
-            return this;
-        }
-
-        /**
-         * Build {@link SubscribeData} given the current requests made on the
-         * builder.
-         */
-        public SubscribeData build() {
-            return new SubscribeData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
-                    mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
-        }
-    }
-}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.java b/wifi/java/android/net/wifi/nan/SubscribeSettings.java
deleted file mode 100644
index 5c4f8fb..0000000
--- a/wifi/java/android/net/wifi/nan/SubscribeSettings.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Defines the settings (configuration) for a NAN subscribe session. Built using
- * {@link SubscribeSettings.Builder}. Subscribe is done using
- * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
- * or {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
- *
- * @hide PROPOSED_NAN_API
- */
-public class SubscribeSettings implements Parcelable {
-
-    /**
-     * Defines a passive subscribe session - i.e. a subscribe session where
-     * subscribe packets are not transmitted over-the-air and the device listens
-     * and matches to transmitted publish packets. Configuration is done using
-     * {@link SubscribeSettings.Builder#setSubscribeType(int)}.
-     */
-    public static final int SUBSCRIBE_TYPE_PASSIVE = 0;
-
-    /**
-     * Defines an active subscribe session - i.e. a subscribe session where
-     * subscribe packets are transmitted over-the-air. Configuration is done
-     * using {@link SubscribeSettings.Builder#setSubscribeType(int)}.
-     */
-    public static final int SUBSCRIBE_TYPE_ACTIVE = 1;
-
-    /**
-     * @hide
-     */
-    public final int mSubscribeType;
-
-    /**
-     * @hide
-     */
-    public final int mSubscribeCount;
-
-    /**
-     * @hide
-     */
-    public final int mTtlSec;
-
-    private SubscribeSettings(int subscribeType, int publichCount, int ttlSec) {
-        mSubscribeType = subscribeType;
-        mSubscribeCount = publichCount;
-        mTtlSec = ttlSec;
-    }
-
-    @Override
-    public String toString() {
-        return "SubscribeSettings [mSubscribeType=" + mSubscribeType + ", mSubscribeCount="
-                + mSubscribeCount + ", mTtlSec=" + mTtlSec + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSubscribeType);
-        dest.writeInt(mSubscribeCount);
-        dest.writeInt(mTtlSec);
-    }
-
-    public static final Creator<SubscribeSettings> CREATOR = new Creator<SubscribeSettings>() {
-        @Override
-        public SubscribeSettings[] newArray(int size) {
-            return new SubscribeSettings[size];
-        }
-
-        @Override
-        public SubscribeSettings createFromParcel(Parcel in) {
-            int subscribeType = in.readInt();
-            int subscribeCount = in.readInt();
-            int ttlSec = in.readInt();
-            return new SubscribeSettings(subscribeType, subscribeCount, ttlSec);
-        }
-    };
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof SubscribeSettings)) {
-            return false;
-        }
-
-        SubscribeSettings lhs = (SubscribeSettings) o;
-
-        return mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
-                && mTtlSec == lhs.mTtlSec;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + mSubscribeType;
-        result = 31 * result + mSubscribeCount;
-        result = 31 * result + mTtlSec;
-
-        return result;
-    }
-
-    /**
-     * Builder used to build {@link SubscribeSettings} objects.
-     */
-    public static final class Builder {
-        int mSubscribeType;
-        int mSubscribeCount;
-        int mTtlSec;
-
-        /**
-         * Sets the type of the subscribe session: active (subscribe packets are
-         * transmitted over-the-air), or passive (no subscribe packets are
-         * transmitted, a match is made against a solicited/active publish
-         * session whose packets are transmitted over-the-air).
-         *
-         * @param subscribeType Subscribe session type: active (
-         *            {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}) or
-         *            passive ( {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}
-         *            ).
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setSubscribeType(int subscribeType) {
-            if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) {
-                throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType);
-            }
-            mSubscribeType = subscribeType;
-            return this;
-        }
-
-        /**
-         * Sets the number of times an active (
-         * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
-         * session will transmit a packet. When the count is reached an event
-         * will be generated for
-         * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
-         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
-         *
-         * @param subscribeCount Number of subscribe packets to transmit.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setSubscribeCount(int subscribeCount) {
-            if (subscribeCount < 0) {
-                throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
-            }
-            mSubscribeCount = subscribeCount;
-            return this;
-        }
-
-        /**
-         * Sets the time interval (in seconds) an active (
-         * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
-         * session will be alive - i.e. transmitting a packet. When the TTL is
-         * reached an event will be generated for
-         * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
-         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
-         *
-         * @param ttlSec Lifetime of a subscribe session in seconds.
-         * @return The builder to facilitate chaining
-         *         {@code builder.setXXX(..).setXXX(..)}.
-         */
-        public Builder setTtlSec(int ttlSec) {
-            if (ttlSec < 0) {
-                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
-            }
-            mTtlSec = ttlSec;
-            return this;
-        }
-
-        /**
-         * Build {@link SubscribeSettings} given the current requests made on
-         * the builder.
-         */
-        public SubscribeSettings build() {
-            return new SubscribeSettings(mSubscribeType, mSubscribeCount, mTtlSec);
-        }
-    }
-}
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
index ea8785a..2c5aab4 100644
--- a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
@@ -16,11 +16,15 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.Nullable;
+
 import libcore.io.Memory;
 
 import java.nio.BufferOverflowException;
 import java.nio.ByteOrder;
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 
 /**
  * Utility class to construct and parse byte arrays using the TLV format -
@@ -50,8 +54,7 @@
      * Values are added to the structure using the {@code TlvConstructor.put*()}
      * methods.
      * <p>
-     * The final byte array is obtained using {@link TlvConstructor#getArray()}
-     * and {@link TlvConstructor#getActualLength()} methods.
+     * The final byte array is obtained using {@link TlvConstructor#getArray()}.
      */
     public static class TlvConstructor {
         private int mTypeSize;
@@ -88,9 +91,9 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor wrap(byte[] array) {
+        public TlvConstructor wrap(@Nullable byte[] array) {
             mArray = array;
-            mArrayLength = array.length;
+            mArrayLength = (array == null) ? 0 : array.length;
             return this;
         }
 
@@ -137,10 +140,13 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) {
+        public TlvConstructor putByteArray(int type, @Nullable byte[] array, int offset,
+                int length) {
             checkLength(length);
             addHeader(type, length);
-            System.arraycopy(array, offset, mArray, mPosition, length);
+            if (length != 0) {
+                System.arraycopy(array, offset, mArray, mPosition, length);
+            }
             mPosition += length;
             return this;
         }
@@ -155,8 +161,8 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putByteArray(int type, byte[] array) {
-            return putByteArray(type, array, 0, array.length);
+        public TlvConstructor putByteArray(int type, @Nullable byte[] array) {
+            return putByteArray(type, array, 0, (array == null) ? 0 : array.length);
         }
 
         /**
@@ -223,23 +229,25 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putString(int type, String data) {
-            return putByteArray(type, data.getBytes(), 0, data.length());
+        public TlvConstructor putString(int type, @Nullable String data) {
+            byte[] bytes = null;
+            int length = 0;
+            if (data != null) {
+                bytes = data.getBytes();
+                length = bytes.length;
+            }
+            return putByteArray(type, bytes, 0, length);
         }
 
         /**
-         * Returns the constructed TLV formatted byte-array. Note that the
-         * returned array is the fully wrapped (
-         * {@link TlvConstructor#wrap(byte[])}) or allocated (
-         * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily
-         * the actual size of the formatted data. Use
-         * {@link TlvConstructor#getActualLength()} to obtain the size of the
-         * formatted data.
+         * Returns the constructed TLV formatted byte-array. This array is a copy of the wrapped
+         * or allocated array - truncated to just the significant bytes - i.e. those written into
+         * the (T)LV.
          *
          * @return The byte array containing the TLV formatted structure.
          */
         public byte[] getArray() {
-            return mArray;
+            return Arrays.copyOf(mArray, getActualLength());
         }
 
         /**
@@ -249,7 +257,7 @@
          *
          * @return The size of the TLV formatted portion of the byte array.
          */
-        public int getActualLength() {
+        private int getActualLength() {
             return mPosition;
         }
 
@@ -287,75 +295,75 @@
          * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of
          * this field is undefined.
          */
-        public int mType;
+        public int type;
 
         /**
          * The Length (L) field of the current TLV element.
          */
-        public int mLength;
+        public int length;
 
         /**
          * The Value (V) field - a raw byte array representing the current TLV
-         * element where the entry starts at {@link TlvElement#mOffset}.
+         * element where the entry starts at {@link TlvElement#offset}.
          */
-        public byte[] mRefArray;
+        public byte[] refArray;
 
         /**
-         * The offset to be used into {@link TlvElement#mRefArray} to access the
+         * The offset to be used into {@link TlvElement#refArray} to access the
          * raw data representing the current TLV element.
          */
-        public int mOffset;
+        public int offset;
 
-        private TlvElement(int type, int length, byte[] refArray, int offset) {
-            mType = type;
-            mLength = length;
-            mRefArray = refArray;
-            mOffset = offset;
+        private TlvElement(int type, int length, @Nullable byte[] refArray, int offset) {
+            this.type = type;
+            this.length = length;
+            this.refArray = refArray;
+            this.offset = offset;
         }
 
         /**
          * Utility function to return a byte representation of a TLV element of
          * length 1. Note: an attempt to call this function on a TLV item whose
-         * {@link TlvElement#mLength} is != 1 will result in an exception.
+         * {@link TlvElement#length} is != 1 will result in an exception.
          *
          * @return byte representation of current TLV element.
          */
         public byte getByte() {
-            if (mLength != 1) {
+            if (length != 1) {
                 throw new IllegalArgumentException(
-                        "Accesing a byte from a TLV element of length " + mLength);
+                        "Accesing a byte from a TLV element of length " + length);
             }
-            return mRefArray[mOffset];
+            return refArray[offset];
         }
 
         /**
          * Utility function to return a short representation of a TLV element of
          * length 2. Note: an attempt to call this function on a TLV item whose
-         * {@link TlvElement#mLength} is != 2 will result in an exception.
+         * {@link TlvElement#length} is != 2 will result in an exception.
          *
          * @return short representation of current TLV element.
          */
         public short getShort() {
-            if (mLength != 2) {
+            if (length != 2) {
                 throw new IllegalArgumentException(
-                        "Accesing a short from a TLV element of length " + mLength);
+                        "Accesing a short from a TLV element of length " + length);
             }
-            return Memory.peekShort(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+            return Memory.peekShort(refArray, offset, ByteOrder.BIG_ENDIAN);
         }
 
         /**
          * Utility function to return an integer representation of a TLV element
          * of length 4. Note: an attempt to call this function on a TLV item
-         * whose {@link TlvElement#mLength} is != 4 will result in an exception.
+         * whose {@link TlvElement#length} is != 4 will result in an exception.
          *
          * @return integer representation of current TLV element.
          */
         public int getInt() {
-            if (mLength != 4) {
+            if (length != 4) {
                 throw new IllegalArgumentException(
-                        "Accesing an int from a TLV element of length " + mLength);
+                        "Accesing an int from a TLV element of length " + length);
             }
-            return Memory.peekInt(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+            return Memory.peekInt(refArray, offset, ByteOrder.BIG_ENDIAN);
         }
 
         /**
@@ -364,7 +372,7 @@
          * @return String repersentation of the current TLV element.
          */
         public String getString() {
-            return new String(mRefArray, mOffset, mLength);
+            return new String(refArray, offset, length);
         }
     }
 
@@ -388,10 +396,8 @@
          * @param lengthSize Number of bytes sued for the Length (L) field.
          *            Values values are 1 or 2 bytes.
          * @param array The TLV formatted byte-array to parse.
-         * @param length The number of bytes of the array to be used in the
-         *            parsing.
          */
-        public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) {
+        public TlvIterable(int typeSize, int lengthSize, @Nullable byte[] array) {
             if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
                 throw new IllegalArgumentException(
                         "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
@@ -399,7 +405,7 @@
             mTypeSize = typeSize;
             mLengthSize = lengthSize;
             mArray = array;
-            mArrayLength = length;
+            mArrayLength = (array == null) ? 0 : array.length;
         }
 
         /**
@@ -420,21 +426,21 @@
                 first = false;
                 builder.append(" (");
                 if (mTypeSize != 0) {
-                    builder.append("T=" + tlv.mType + ",");
+                    builder.append("T=" + tlv.type + ",");
                 }
-                builder.append("L=" + tlv.mLength + ") ");
-                if (tlv.mLength == 0) {
+                builder.append("L=" + tlv.length + ") ");
+                if (tlv.length == 0) {
                     builder.append("<null>");
-                } else if (tlv.mLength == 1) {
+                } else if (tlv.length == 1) {
                     builder.append(tlv.getByte());
-                } else if (tlv.mLength == 2) {
+                } else if (tlv.length == 2) {
                     builder.append(tlv.getShort());
-                } else if (tlv.mLength == 4) {
+                } else if (tlv.length == 4) {
                     builder.append(tlv.getInt());
                 } else {
                     builder.append("<bytes>");
                 }
-                if (tlv.mLength != 0) {
+                if (tlv.length != 0) {
                     builder.append(" (S='" + tlv.getString() + "')");
                 }
             }
@@ -459,6 +465,10 @@
 
                 @Override
                 public TlvElement next() {
+                    if (!hasNext()) {
+                        throw new NoSuchElementException();
+                    }
+
                     int type = 0;
                     if (mTypeSize == 1) {
                         type = mArray[mOffset];
@@ -487,4 +497,40 @@
             };
         }
     }
+
+    /**
+     * Validates that a (T)LV array is constructed correctly. I.e. that its specified Length
+     * fields correctly fill the specified length (and do not overshoot).
+     *
+     * @param array The (T)LV array to verify.
+     * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
+     * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
+     * @return A boolean indicating whether the array is valid (true) or invalid (false).
+     */
+    public static boolean isValid(@Nullable byte[] array, int typeSize, int lengthSize) {
+        if (typeSize < 0 || typeSize > 2) {
+            throw new IllegalArgumentException(
+                    "Invalid arguments - typeSize must be 0, 1, or 2: typeSize=" + typeSize);
+        }
+        if (lengthSize <= 0 || lengthSize > 2) {
+            throw new IllegalArgumentException(
+                    "Invalid arguments - lengthSize must be 1 or 2: lengthSize=" + lengthSize);
+        }
+        if (array == null) {
+            return true;
+        }
+
+        int nextTlvIndex = 0;
+        while (nextTlvIndex + typeSize + lengthSize <= array.length) {
+            nextTlvIndex += typeSize;
+            if (lengthSize == 1) {
+                nextTlvIndex += lengthSize + array[nextTlvIndex];
+            } else {
+                nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex,
+                        ByteOrder.BIG_ENDIAN);
+            }
+        }
+
+        return nextTlvIndex == array.length;
+    }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
new file mode 100644
index 0000000..6e714f1
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for NAN events callbacks. Should be extended by applications and set when calling
+ * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}. These are callbacks
+ * applying to the NAN connection as a whole - not to specific publish or subscribe sessions -
+ * for that see {@link WifiNanSessionCallback}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanEventCallback {
+    /** @hide */
+    @IntDef({
+            REASON_INVALID_ARGS, REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG, REASON_OTHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventReasonCodes {
+    }
+
+    /**
+     * Indicates invalid argument in the requested operation. Failure reason flag for
+     * {@link WifiNanEventCallback#onConnectFail(int)}.
+     */
+    public static final int REASON_INVALID_ARGS = 1000;
+
+    /**
+     * Indicates that a {@link ConfigRequest} passed in
+     * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}
+     * couldn't be applied since other connections already exist with an incompatible
+     * configurations. Failure reason flag for {@link WifiNanEventCallback#onConnectFail(int)}.
+     */
+    public static final int REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG = 1001;
+
+    /**
+     * Indicates an unspecified error occurred during the operation. Failure reason flag for
+     * {@link WifiNanEventCallback#onConnectFail(int)}.
+     */
+    public static final int REASON_OTHER = 1002;
+
+    /**
+     * Called when NAN connect operation
+     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}
+     * is completed and that we can now start discovery sessions or connections.
+     */
+    public void onConnectSuccess() {
+        /* empty */
+    }
+
+    /**
+     * Called when NAN connect operation
+     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)} failed.
+     *
+     * @param reason Failure reason code, see
+     *            {@code WifiNanEventCallback.REASON_*}.
+     */
+    public void onConnectFail(@EventReasonCodes int reason) {
+        /* empty */
+    }
+
+    /**
+     * Called when NAN identity (the MAC address representing our NAN discovery interface) has
+     * changed. Change may be due to device joining a cluster, starting a cluster, or discovery
+     * interface change (addresses are randomized at regular intervals). The implication is that
+     * peers you've been communicating with may no longer recognize you and you need to
+     * re-establish your identity - e.g. by starting a discovery session. This actual MAC address
+     * of the interface may also be useful if the application uses alternative (non-NAN)
+     * discovery but needs to set up a NAN connection. The provided NAN discovery interface MAC
+     * address can then be used in
+     * {@link WifiNanManager#createNetworkSpecifier(int, byte[], byte[])}.
+     * <p>
+     *     This callback is only called if the NAN connection enables it using
+     *     {@link ConfigRequest.Builder#setEnableIdentityChangeCallback(boolean)} in
+     *     {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}
+     *     . It is disabled by default since it may result in additional wake-ups of the host -
+     *     increasing power.
+     *
+     * @param mac The MAC address of the NAN discovery interface. The application must have the
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to get the actual MAC address,
+     *            otherwise all 0's will be provided.
+     */
+    public void onIdentityChanged(byte[] mac) {
+        /* empty */
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
deleted file mode 100644
index 9e6ed4e..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * Base class for NAN events callbacks. Should be extended by applications
- * wanting notifications. These are callbacks applying to the NAN connection as
- * a whole - not to specific publish or subscribe sessions - for that see
- * {@link WifiNanSessionListener}.
- * <p>
- * During registration specify which specific events are desired using a set of
- * {@code NanEventListener.LISTEN_*} flags OR'd together. Only those events will
- * be delivered to the registered listener. Override those callbacks
- * {@code NanEventListener.on*} for the registered events.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanEventListener {
-    private static final String TAG = "WifiNanEventListener";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false; // STOPSHIP if true
-
-    /**
-     * Configuration completion callback event registration flag. Corresponding
-     * callback is {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)}.
-     */
-    public static final int LISTEN_CONFIG_COMPLETED = 0x1 << 0;
-
-    /**
-     * Configuration failed callback event registration flag. Corresponding
-     * callback is
-     * {@link WifiNanEventListener#onConfigFailed(ConfigRequest, int)}.
-     */
-    public static final int LISTEN_CONFIG_FAILED = 0x1 << 1;
-
-    /**
-     * NAN cluster is down callback event registration flag. Corresponding
-     * callback is {@link WifiNanEventListener#onNanDown(int)}.
-     */
-    public static final int LISTEN_NAN_DOWN = 0x1 << 2;
-
-    /**
-     * NAN identity has changed event registration flag. This may be due to
-     * joining a cluster, starting a cluster, or discovery interface change. The
-     * implication is that peers you've been communicating with may no longer
-     * recognize you and you need to re-establish your identity. Corresponding
-     * callback is {@link WifiNanEventListener#onIdentityChanged()}.
-     */
-    public static final int LISTEN_IDENTITY_CHANGED = 0x1 << 3;
-
-    private final Handler mHandler;
-
-    /**
-     * Constructs a {@link WifiNanEventListener} using the looper of the current
-     * thread. I.e. all callbacks will be delivered on the current thread.
-     */
-    public WifiNanEventListener() {
-        this(Looper.myLooper());
-    }
-
-    /**
-     * Constructs a {@link WifiNanEventListener} using the specified looper. I.e.
-     * all callbacks will delivered on the thread of the specified looper.
-     *
-     * @param looper The looper on which to execute the callbacks.
-     */
-    public WifiNanEventListener(Looper looper) {
-        if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
-        mHandler = new Handler(looper) {
-            @Override
-            public void handleMessage(Message msg) {
-                if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
-                switch (msg.what) {
-                    case LISTEN_CONFIG_COMPLETED:
-                        WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj);
-                        break;
-                    case LISTEN_CONFIG_FAILED:
-                        WifiNanEventListener.this.onConfigFailed((ConfigRequest) msg.obj, msg.arg1);
-                        break;
-                    case LISTEN_NAN_DOWN:
-                        WifiNanEventListener.this.onNanDown(msg.arg1);
-                        break;
-                    case LISTEN_IDENTITY_CHANGED:
-                        WifiNanEventListener.this.onIdentityChanged();
-                        break;
-                }
-            }
-        };
-    }
-
-    /**
-     * Called when NAN configuration is completed. Event will only be delivered
-     * if registered using {@link WifiNanEventListener#LISTEN_CONFIG_COMPLETED}. A
-     * dummy (empty implementation printing out a warning). Make sure to
-     * override if registered.
-     *
-     * @param completedConfig The actual configuration request which was
-     *            completed. Note that it may be different from that requested
-     *            by the application. The service combines configuration
-     *            requests from all applications.
-     */
-    public void onConfigCompleted(ConfigRequest completedConfig) {
-        Log.w(TAG, "onConfigCompleted: called in stub - override if interested or disable");
-    }
-
-    /**
-     * Called when NAN configuration failed. Event will only be delivered if
-     * registered using {@link WifiNanEventListener#LISTEN_CONFIG_FAILED}. A dummy
-     * (empty implementation printing out a warning). Make sure to override if
-     * registered.
-     *
-     * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}.
-     */
-    public void onConfigFailed(ConfigRequest failedConfig, int reason) {
-        Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable");
-    }
-
-    /**
-     * Called when NAN cluster is down. Event will only be delivered if
-     * registered using {@link WifiNanEventListener#LISTEN_NAN_DOWN}. A dummy (empty
-     * implementation printing out a warning). Make sure to override if
-     * registered.
-     *
-     * @param reason Reason code for event, see {@code NanSessionListener.FAIL_*}.
-     */
-    public void onNanDown(int reason) {
-        Log.w(TAG, "onNanDown: called in stub - override if interested or disable");
-    }
-
-    /**
-     * Called when NAN identity has changed. This may be due to joining a
-     * cluster, starting a cluster, or discovery interface change. The
-     * implication is that peers you've been communicating with may no longer
-     * recognize you and you need to re-establish your identity. Event will only
-     * be delivered if registered using
-     * {@link WifiNanEventListener#LISTEN_IDENTITY_CHANGED}. A dummy (empty
-     * implementation printing out a warning). Make sure to override if
-     * registered.
-     */
-    public void onIdentityChanged() {
-        if (VDBG) Log.v(TAG, "onIdentityChanged: called in stub - override if interested");
-    }
-
-    /**
-     * {@hide}
-     */
-    public IWifiNanEventListener callback = new IWifiNanEventListener.Stub() {
-        @Override
-        public void onConfigCompleted(ConfigRequest completedConfig) {
-            if (VDBG) Log.v(TAG, "onConfigCompleted: configRequest=" + completedConfig);
-
-            Message msg = mHandler.obtainMessage(LISTEN_CONFIG_COMPLETED);
-            msg.obj = completedConfig;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onConfigFailed(ConfigRequest failedConfig, int reason) {
-            if (VDBG) {
-                Log.v(TAG, "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason);
-            }
-
-            Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED);
-            msg.arg1 = reason;
-            msg.obj = failedConfig;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onNanDown(int reason) {
-            if (VDBG) Log.v(TAG, "onNanDown: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_NAN_DOWN);
-            msg.arg1 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onIdentityChanged() {
-            if (VDBG) Log.v(TAG, "onIdentityChanged");
-
-            Message msg = mHandler.obtainMessage(LISTEN_IDENTITY_CHANGED);
-            mHandler.sendMessage(msg);
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index 1b78beb..82d22bc 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -16,23 +16,103 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkRequest;
+import android.net.wifi.RttManager;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
+import android.util.Base64;
 import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.system.CloseGuard;
+
+import libcore.util.HexEncoding;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
 
 /**
- * This class provides the primary API for managing Wi-Fi NAN operation:
- * including discovery and data-links. Get an instance of this class by calling
+ * This class provides the primary API for managing Wi-Fi NAN operations:
+ * discovery and peer-to-peer data connections. Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(String)
  * Context.getSystemService(Context.WIFI_NAN_SERVICE)}.
  * <p>
  * The class provides access to:
  * <ul>
- * <li>Configure a NAN connection and register for events.
- * <li>Create publish and subscribe sessions.
- * <li>Create NAN network specifier to be used to create a NAN network.
+ * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to
+ * {@link #connect(Looper, WifiNanEventCallback)}.
+ * <li>Create discovery sessions (publish or subscribe sessions).
+ * Refer to {@link #publish(PublishConfig, WifiNanSessionCallback)} and
+ * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)}.
+ * <li>Create a NAN network specifier to be used with
+ * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
+ * to set-up a NAN connection with a peer. Refer to
+ * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])} and
+ * {@link #createNetworkSpecifier(int, byte[], byte[])}.
  * </ul>
+ * <p>
+ *     NAN may not be usable when Wi-Fi is disabled (and other conditions). To validate that
+ *     the functionality is available use the {@link #isUsageEnabled()} function. To track
+ *     changes in NAN usability register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
+ *     Note that this broadcast is not sticky - you should register for it and then check the
+ *     above API to avoid a race condition.
+ * <p>
+ *     An application must use {@link #connect(Looper, WifiNanEventCallback)} to initialize a NAN
+ *     cluster - before making any other NAN operation. NAN cluster membership is a device-wide
+ *     operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or
+ *     starts one if none can be found). Information about connection success (or failure) are
+ *     returned in callbacks of {@link WifiNanEventCallback}. Proceed with NAN discovery or
+ *     connection setup only after receiving confirmation that NAN connection succeeded -
+ *     {@link WifiNanEventCallback#onConnectSuccess()}.
+ *     When an application is finished using NAN it <b>must</b> use the {@link #disconnect()} API
+ *     to indicate to the NAN service that the device may disconnect from the NAN cluster. The
+ *     device will actually disconnect from the NAN cluster once the last application disconnects.
+ * <p>
+ *     Once a NAN connection is confirmed use the
+ *     {@link #publish(PublishConfig, WifiNanSessionCallback)} or
+ *     {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)} to create publish or subscribe
+ *     NAN discovery sessions. Events are called on the provided callback object
+ *     {@link WifiNanSessionCallback}. Specifically, the
+ *     {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} and
+ *     {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} return
+ *     {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession} objects respectively on
+ *     which additional session operations can be performed, e.g. updating the session
+ *     {@link WifiNanPublishSession#updatePublish(PublishConfig)} and
+ *     {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. Sessions can also be
+ *     used to send messages using the {@link WifiNanSession#sendMessage(int, byte[], int)} APIs.
+ *     When an application is finished with a discovery session it <b>must</b> terminate it using
+ *     the {@link WifiNanSession#terminate()} API.
+ * <p>
+ *    Creating connections between NAN devices is managed by the standard
+ *    {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}.
+ *    The {@link NetworkRequest} object should be constructed with:
+ *    <ul>
+ *        <li>{@link NetworkRequest.Builder#addTransportType(int)} of
+ *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+ *        <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
+ *        {@link #createNetworkSpecifier(int, byte[], byte[])} or
+ *        {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}.
+ *    </ul>
  *
  * @hide PROPOSED_NAN_API
  */
@@ -41,292 +121,1080 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private IBinder mBinder;
-
-    private IWifiNanManager mService;
+    private static final int INVALID_CLIENT_ID = 0;
 
     /**
-     * {@hide}
+     * Keys used to generate a Network Specifier for the NAN network request. The network specifier
+     * is formatted as a JSON string.
      */
-    public WifiNanManager(IWifiNanManager service) {
+
+    /**
+     * TYPE_1A: role, client_id, session_id, peer_id, token
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_1A = 0;
+
+    /**
+     * TYPE_1B: role, client_id, session_id, peer_id [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_1B = 1;
+
+    /**
+     * TYPE_1C: role, client_id, session_id, token [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_1C = 2;
+
+    /**
+     * TYPE_1C: role, client_id, session_id [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_1D = 3;
+
+    /**
+     * TYPE_2A: role, client_id, peer_mac, token
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_2A = 4;
+
+    /**
+     * TYPE_2B: role, client_id, peer_mac [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_2B = 5;
+
+    /**
+     * TYPE_2C: role, client_id, token [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_2C = 6;
+
+    /**
+     * TYPE_2D: role, client_id [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_2D = 7;
+
+    /** @hide */
+    public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_2D;
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_TYPE = "type";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_ROLE = "role";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac";
+
+    /** @hide */
+    public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token";
+
+    /**
+     * Broadcast intent action to indicate whether Wi-Fi NAN is enabled or
+     * disabled. An extra {@link #EXTRA_WIFI_STATE} provides the state
+     * information as int using {@link #WIFI_NAN_STATE_DISABLED} and
+     * {@link #WIFI_NAN_STATE_ENABLED} constants. This broadcast is <b>not</b> sticky,
+     * use the {@link #isUsageEnabled()} API after registering the broadcast to check the current
+     * state of Wi-Fi NAN.
+     *
+     * @see #EXTRA_WIFI_STATE
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WIFI_NAN_STATE_CHANGED =
+            "android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED";
+
+    /**
+     * The lookup key for an int value indicating whether Wi-Fi NAN is enabled or
+     * disabled. Retrieve it with
+     * {@link android.content.Intent#getIntExtra(String,int)}.
+     *
+     * @see #WIFI_NAN_STATE_DISABLED
+     * @see #WIFI_NAN_STATE_ENABLED
+     */
+    public static final String EXTRA_WIFI_STATE = "android.net.wifi.nan.extra.WIFI_STATE";
+
+    /**
+     * Wi-Fi NAN is disabled.
+     *
+     * @see #ACTION_WIFI_NAN_STATE_CHANGED
+     */
+    public static final int WIFI_NAN_STATE_DISABLED = 1;
+
+    /**
+     * Wi-Fi NAN is enabled.
+     *
+     * @see #ACTION_WIFI_NAN_STATE_CHANGED
+     */
+    public static final int WIFI_NAN_STATE_ENABLED = 2;
+
+    /** @hide */
+    @IntDef({
+            WIFI_NAN_DATA_PATH_ROLE_INITIATOR, WIFI_NAN_DATA_PATH_ROLE_RESPONDER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataPathRole {
+    }
+
+    /**
+     * Connection creation role is that of INITIATOR. Used to create a network specifier string
+     * when requesting a NAN network.
+     *
+     * @see WifiNanSession#createNetworkSpecifier(int, int, byte[])
+     * @see #createNetworkSpecifier(int, byte[], byte[])
+     */
+    public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0;
+
+    /**
+     * Connection creation role is that of RESPONDER. Used to create a network specifier string
+     * when requesting a NAN network.
+     *
+     * @see WifiNanSession#createNetworkSpecifier(int, int, byte[])
+     * @see #createNetworkSpecifier(int, byte[], byte[])
+     */
+    public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1;
+
+    private final Context mContext;
+    private final IWifiNanManager mService;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private final Object mLock = new Object(); // lock access to the following vars
+
+    @GuardedBy("mLock")
+    private final IBinder mBinder = new Binder();
+
+    @GuardedBy("mLock")
+    private int mClientId = INVALID_CLIENT_ID;
+
+    @GuardedBy("mLock")
+    private Looper mLooper;
+
+    @GuardedBy("mLock")
+    private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>();
+
+    /** @hide */
+    public WifiNanManager(Context context, IWifiNanManager service) {
+        mContext = context;
         mService = service;
     }
 
     /**
-     * Re-connect to the Wi-Fi NAN service - enabling the application to execute
-     * {@link WifiNanManager} APIs. Application don't normally need to call this
-     * API since it is executed in the constructor. However, applications which
-     * have explicitly {@link WifiNanManager#disconnect()} need to call this
-     * function to re-connect.
+     * Enable the usage of the NAN API. Doesn't actually turn on NAN cluster formation - that only
+     * happens when a connection is made. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast will be
+     * triggered.
      *
-     * @param listener A listener extended from {@link WifiNanEventListener}.
-     * @param events The set of events to be delivered to the {@code listener}.
-     *            OR'd event flags from {@link WifiNanEventListener
-     *            NanEventListener.LISTEN*}.
+     * @hide
      */
-    public void connect(WifiNanEventListener listener, int events) {
+    public void enableUsage() {
         try {
-            if (VDBG) Log.v(TAG, "connect()");
-            if (listener == null) {
-                throw new IllegalArgumentException("Invalid listener - must not be null");
-            }
-            if (mBinder == null) {
-                mBinder = new Binder();
-            }
-            mService.connect(mBinder, listener.callback, events);
+            mService.enableUsage();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Disconnect from the Wi-Fi NAN service and destroy all outstanding
-     * operations - i.e. all publish and subscribes are terminated, any
-     * outstanding data-link is shut-down, and all requested NAN configurations
-     * are cancelled.
+     * Disable the usage of the NAN API. All attempts to connect() will be rejected. All open
+     * connections and sessions will be terminated. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast
+     * will be triggered.
+     *
+     * @hide
+     */
+    public void disableUsage() {
+        try {
+            mService.disableUsage();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current status of NAN API: whether or not usage is enabled. To track changes
+     * in the state of NAN API register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
+     *
+     * @return A boolean indicating whether the app can use the NAN API (true)
+     *         or not (false).
+     */
+    public boolean isUsageEnabled() {
+        try {
+            return mService.isUsageEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Connect to the Wi-Fi NAN service - enabling the application to create discovery session or
+     * create connection to peers. The device will connect to an existing cluster if it can find
+     * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+     * (e.g. successful connection to a cluster) are provided to the {@code callback} object.
+     * An application <b>must</b> call {@link #disconnect()} when done with the Wi-Fi NAN
+     * connection.
      * <p>
-     * An application may then re-connect using
-     * {@link WifiNanManager#connect(WifiNanEventListener, int)} .
+     * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster
+     * than this function will simply indicate success immediately.
+     *
+     * @param looper The Looper on which to execute all callbacks related to the
+     *            connection - including all sessions opened as part of this
+     *            connection.
+     * @param callback A callback extended from {@link WifiNanEventCallback}.
+     */
+    public void connect(@NonNull Looper looper, @NonNull WifiNanEventCallback callback) {
+        connect(looper, null, callback);
+    }
+
+    /**
+     * Connect to the Wi-Fi NAN service - enabling the application to create discovery session or
+     * create connection to peers. The device will connect to an existing cluster if it can find
+     * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+     * (e.g. successful connection to a cluster) are provided to the {@code callback} object.
+     * An application <b>must</b> call {@link #disconnect()} when done with the Wi-Fi NAN
+     * connection. Allows requesting a specific configuration using {@link ConfigRequest}. If not
+     * necessary (default configuration should usually work) use the
+     * {@link #connect(Looper, WifiNanEventCallback)} method instead.
+     * <p>
+     * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster
+     * than this function will simply indicate success immediately.
+     *
+     * @param looper The Looper on which to execute all callbacks related to the
+     *            connection - including all sessions opened as part of this
+     *            connection.
+     * @param configRequest The requested NAN configuration.
+     * @param callback A callback extended from {@link WifiNanEventCallback}.
+     */
+    public void connect(@NonNull Looper looper, @Nullable ConfigRequest configRequest,
+            @NonNull WifiNanEventCallback callback) {
+        if (VDBG) {
+            Log.v(TAG, "connect(): looper=" + looper + ", callback=" + callback + ", configRequest="
+                    + configRequest);
+        }
+
+        synchronized (mLock) {
+            mLooper = looper;
+
+            try {
+                mClientId = mService.connect(mBinder, mContext.getOpPackageName(),
+                        new WifiNanEventCallbackProxy(this, looper, callback), configRequest);
+            } catch (RemoteException e) {
+                mClientId = INVALID_CLIENT_ID;
+                mLooper = null;
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        mCloseGuard.open("disconnect");
+    }
+
+    /**
+     * Disconnect from the Wi-Fi NAN service and, if no other applications are connected to NAN,
+     * also disconnect from the NAN cluster. This method destroys all outstanding operations -
+     * i.e. all publish and subscribes are terminated, and any outstanding data-links are
+     * shut-down. However, it is good practice to terminate these discovery sessions and
+     * connections explicitly before a disconnect.
+     * <p>
+     * An application may re-connect after a disconnect using
+     * {@link WifiNanManager#connect(Looper, WifiNanEventCallback)} .
      */
     public void disconnect() {
-        try {
-            if (VDBG) Log.v(TAG, "disconnect()");
-            mService.disconnect(mBinder);
-            mBinder = null;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
+        if (VDBG) Log.v(TAG, "disconnect()");
 
-    /**
-     * Requests a NAN configuration, specified by {@link ConfigRequest}. Note
-     * that NAN is a shared resource and the device can only be a member of a
-     * single cluster. Thus the service may merge configuration requests from
-     * multiple applications and configure NAN differently from individual
-     * requests.
-     * <p>
-     * The {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)} will be
-     * called when configuration is completed (if a listener is registered for
-     * this specific event).
-     *
-     * @param configRequest The requested NAN configuration.
-     */
-    public void requestConfig(ConfigRequest configRequest) {
-        if (VDBG) Log.v(TAG, "requestConfig(): configRequest=" + configRequest);
-        try {
-            mService.requestConfig(configRequest);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Request a NAN publish session. The results of the publish session
-     * operation will result in callbacks to the indicated listener:
-     * {@link WifiNanSessionListener NanSessionListener.on*}.
-     *
-     * @param publishData The {@link PublishData} specifying the contents of the
-     *            publish session.
-     * @param publishSettings The {@link PublishSettings} specifying the
-     *            settings for the publish session.
-     * @param listener The {@link WifiNanSessionListener} derived objects to be used
-     *            for the event callbacks specified by {@code events}.
-     * @param events The list of events to be delivered to the {@code listener}
-     *            object. An OR'd value of {@link WifiNanSessionListener
-     *            NanSessionListener.LISTEN_*}.
-     * @return The {@link WifiNanPublishSession} which can be used to further
-     *         control the publish session.
-     */
-    public WifiNanPublishSession publish(PublishData publishData, PublishSettings publishSettings,
-            WifiNanSessionListener listener, int events) {
-        return publishRaw(publishData, publishSettings, listener,
-                events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
-    }
-
-    /**
-     * Same as publish(*) but does not modify the event flag
-     *
-     * @hide
-     */
-    public WifiNanPublishSession publishRaw(PublishData publishData,
-            PublishSettings publishSettings, WifiNanSessionListener listener, int events) {
-        if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
-
-        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
-                && publishData.mRxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
-                    + "publishes (active) can't have an Rx filter");
-        }
-        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
-                && publishData.mTxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
-                    + "publishes (passive) can't have a Tx filter");
-        }
-        if (listener == null) {
-            throw new IllegalArgumentException("Invalid listener - must not be null");
-        }
-
-        int sessionId;
-
-        try {
-            sessionId = mService.createSession(listener.callback, events);
-            if (DBG) Log.d(TAG, "publish: session created - sessionId=" + sessionId);
-            mService.publish(sessionId, publishData, publishSettings);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        return new WifiNanPublishSession(this, sessionId);
-    }
-
-    /**
-     * {@hide}
-     */
-    public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) {
-        if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
-
-        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
-                && publishData.mRxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
-                    + "publishes (active) can't have an Rx filter");
-        }
-        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
-                && publishData.mTxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
-                    + "publishes (passive) can't have a Tx filter");
-        }
-
-        try {
-            mService.publish(sessionId, publishData, publishSettings);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-    /**
-     * Request a NAN subscribe session. The results of the subscribe session
-     * operation will result in callbacks to the indicated listener:
-     * {@link WifiNanSessionListener NanSessionListener.on*}.
-     *
-     * @param subscribeData The {@link SubscribeData} specifying the contents of
-     *            the subscribe session.
-     * @param subscribeSettings The {@link SubscribeSettings} specifying the
-     *            settings for the subscribe session.
-     * @param listener The {@link WifiNanSessionListener} derived objects to be used
-     *            for the event callbacks specified by {@code events}.
-     * @param events The list of events to be delivered to the {@code listener}
-     *            object. An OR'd value of {@link WifiNanSessionListener
-     *            NanSessionListener.LISTEN_*}.
-     * @return The {@link WifiNanSubscribeSession} which can be used to further
-     *         control the subscribe session.
-     */
-    public WifiNanSubscribeSession subscribe(SubscribeData subscribeData,
-            SubscribeSettings subscribeSettings,
-            WifiNanSessionListener listener, int events) {
-        return subscribeRaw(subscribeData, subscribeSettings, listener,
-                events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
-    }
-
-    /**
-     * Same as subscribe(*) but does not modify the event flag
-     *
-     * @hide
-     */
-    public WifiNanSubscribeSession subscribeRaw(SubscribeData subscribeData,
-            SubscribeSettings subscribeSettings, WifiNanSessionListener listener, int events) {
-        if (VDBG) {
-            Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
-        }
-
-        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
-                && subscribeData.mRxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
-        }
-        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
-                && subscribeData.mTxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
-        }
-
-        int sessionId;
-
-        try {
-            sessionId = mService.createSession(listener.callback, events);
-            if (DBG) Log.d(TAG, "subscribe: session created - sessionId=" + sessionId);
-            mService.subscribe(sessionId, subscribeData, subscribeSettings);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        return new WifiNanSubscribeSession(this, sessionId);
-    }
-
-    /**
-     * {@hide}
-     */
-    public void subscribe(int sessionId, SubscribeData subscribeData,
-            SubscribeSettings subscribeSettings) {
-        if (VDBG) {
-            Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
-        }
-
-        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
-                && subscribeData.mRxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
-        }
-        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
-                && subscribeData.mTxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
-        }
-
-        try {
-            mService.subscribe(sessionId, subscribeData, subscribeSettings);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * {@hide}
-     */
-    public void stopSession(int sessionId) {
-        if (DBG) Log.d(TAG, "Stop NAN session #" + sessionId);
-
-        try {
-            mService.stopSession(sessionId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * {@hide}
-     */
-    public void destroySession(int sessionId) {
-        if (DBG) Log.d(TAG, "Destroy NAN session #" + sessionId);
-
-        try {
-            mService.destroySession(sessionId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * {@hide}
-     */
-    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
-            int messageId) {
-        try {
-            if (VDBG) {
-                Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
-                        + ", messageLength=" + messageLength + ", messageId=" + messageId);
+        IBinder binder;
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.w(TAG, "disconnect(): called with invalid client ID - not connected first?");
+                return;
             }
-            mService.sendMessage(sessionId, peerId, message, messageLength, messageId);
+
+            binder = mBinder;
+            clientId = mClientId;
+
+            mLooper = null;
+            mClientId = INVALID_CLIENT_ID;
+        }
+
+        mCloseGuard.close();
+        try {
+            mService.disconnect(clientId, binder);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /** @hide */
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            mCloseGuard.warnIfOpen();
+            disconnect();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Issue a request to the NAN service to create a new NAN publish discovery session, using
+     * the specified {@code publishConfig} configuration. The results of the publish operation
+     * are routed to the callbacks of {@link WifiNanSessionCallback}:
+     * <ul>
+     *     <li>{@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} is called
+     *     when the publish session is created and provides a handle to the session. Further
+     *     operations on the publish session can be executed on that object.
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the publish
+     *     operation failed.
+     * </ul>
+     * <p>
+     * Other results of the publish session operations will also be routed to callbacks
+     * on the {@code callback} object. The resulting publish session can be modified using
+     * {@link WifiNanPublishSession#updatePublish(PublishConfig)}.
+     * <p>
+     *      An application must use the {@link WifiNanSession#terminate()} to terminate the publish
+     *      discovery session once it isn't needed. This will free resources as well terminate
+     *      any on-air transmissions.
+     *
+     * @param publishConfig The {@link PublishConfig} specifying the
+     *            configuration of the requested publish session.
+     * @param callback A {@link WifiNanSessionCallback} derived object to be used for session
+     *                 event callbacks.
+     */
+    public void publish(@NonNull PublishConfig publishConfig,
+            @NonNull WifiNanSessionCallback callback) {
+        if (VDBG) Log.v(TAG, "publish(): config=" + publishConfig);
+
+        int clientId;
+        Looper looper;
+        synchronized (mLock) {
+            if (mLooper == null || mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG, "publish(): called with null looper or invalid client ID - "
+                        + "not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+            looper = mLooper;
+        }
+        try {
+            mService.publish(clientId, publishConfig,
+                    new WifiNanSessionCallbackProxy(this, looper, true, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void updatePublish(int sessionId, PublishConfig publishConfig) {
+        if (VDBG) Log.v(TAG, "updatePublish(): config=" + publishConfig);
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG, "updatePublish(): called with invalid client ID - not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+        try {
+            mService.updatePublish(clientId, sessionId, publishConfig);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Issue a request to the NAN service to create a new NAN subscribe discovery session, using
+     * the specified {@code subscribeConfig} configuration. The results of the subscribe
+     * operation are routed to the callbacks of {@link WifiNanSessionCallback}:
+     * <ul>
+     *     <li>{@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} is called
+     *     when the subscribe session is created and provides a handle to the session. Further
+     *     operations on the subscribe session can be executed on that object.
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the subscribe
+     *     operation failed.
+     * </ul>
+     * <p>
+     * Other results of the subscribe session operations will also be routed to callbacks
+     * on the {@code callback} object. The resulting subscribe session can be modified using
+     * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}.
+     * <p>
+     *      An application must use the {@link WifiNanSession#terminate()} to terminate the
+     *      subscribe discovery session once it isn't needed. This will free resources as well
+     *      terminate any on-air transmissions.
+     *
+     * @param subscribeConfig The {@link SubscribeConfig} specifying the
+     *            configuration of the requested subscribe session.
+     * @param callback A {@link WifiNanSessionCallback} derived object to be used for session
+     *                 event callbacks.
+     */
+    public void subscribe(@NonNull SubscribeConfig subscribeConfig,
+            @NonNull WifiNanSessionCallback callback) {
+        if (VDBG) {
+            Log.v(TAG, "subscribe(): config=" + subscribeConfig);
+        }
+
+        int clientId;
+        Looper looper;
+        synchronized (mLock) {
+            if (mLooper == null || mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG, "subscribe(): called with null looper or invalid client ID - "
+                        + "not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+            looper = mLooper;
+        }
+
+        try {
+            mService.subscribe(clientId, subscribeConfig,
+                    new WifiNanSessionCallbackProxy(this, looper, false, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void updateSubscribe(int sessionId, SubscribeConfig subscribeConfig) {
+        if (VDBG) {
+            Log.v(TAG, "subscribe(): config=" + subscribeConfig);
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG,
+                        "updateSubscribe(): called with invalid client ID - not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+
+        try {
+            mService.updateSubscribe(clientId, sessionId, subscribeConfig);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void terminateSession(int sessionId) {
+        if (DBG) Log.d(TAG, "Terminate NAN session #" + sessionId);
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG,
+                        "terminateSession(): called with invalid client ID - not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+
+        try {
+            mService.terminateSession(clientId, sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void sendMessage(int sessionId, int peerId, byte[] message, int messageId,
+            int retryCount) {
+        if (VDBG) {
+            Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
+                    + ", messageId=" + messageId + ", retryCount=" + retryCount);
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG, "sendMessage(): called with invalid client ID - not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+
+        try {
+            mService.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void startRanging(int sessionId, RttManager.RttParams[] params,
+                             RttManager.RttListener listener) {
+        if (VDBG) {
+            Log.v(TAG, "startRanging: sessionId=" + sessionId + ", " + "params="
+                    + Arrays.toString(params) + ", listener=" + listener);
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG, "startRanging(): called with invalid client ID - not connected first?");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+
+        int rangingKey = 0;
+        try {
+            rangingKey = mService.startRanging(clientId, sessionId,
+                    new RttManager.ParcelableRttParams(params));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        synchronized (mLock) {
+            mRangingListeners.put(rangingKey, listener);
+        }
+    }
+
+    /** @hide */
+    public String createNetworkSpecifier(@DataPathRole int role, int sessionId, int peerId,
+            byte[] token) {
+        if (VDBG) {
+            Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
+                    + ", peerId=" + peerId + ", token=" + token);
+        }
+
+        int type;
+        if (token != null && peerId != 0) {
+            type = NETWORK_SPECIFIER_TYPE_1A;
+        } else if (token == null && peerId != 0) {
+            type = NETWORK_SPECIFIER_TYPE_1B;
+        } else if (token != null && peerId == 0) {
+            type = NETWORK_SPECIFIER_TYPE_1C;
+        } else {
+            type = NETWORK_SPECIFIER_TYPE_1D;
+        }
+
+        if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
+                && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+            throw new IllegalArgumentException(
+                    "createNetworkSpecifier: Invalid 'role' argument when creating a network "
+                            + "specifier");
+        }
+        if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+            if (token == null) {
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
+            }
+            if (peerId == 0) {
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid peer ID (value of 0) - not permitted on "
+                                + "INITIATOR");
+            }
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG,
+                        "createNetworkSpecifier: called with invalid client ID - not connected "
+                                + "first?");
+                return null;
+            }
+
+            clientId = mClientId;
+        }
+
+        JSONObject json;
+        try {
+            json = new JSONObject();
+            json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
+            json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
+            json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
+            json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId);
+            if (peerId != 0) {
+                json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerId);
+            }
+            if (token != null) {
+                json.put(NETWORK_SPECIFIER_KEY_TOKEN,
+                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+            }
+        } catch (JSONException e) {
+            return "";
+        }
+
+        return json.toString();
+    }
+
+    /**
+     * Create a {@link NetworkRequest.Builder#setNetworkSpecifier(String)} for a
+     * WiFi NAN connection to the specified peer. The
+     * {@link NetworkRequest.Builder#addTransportType(int)} should be set to
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+     * <p>
+     *     This API is targeted for applications which can obtain the peer MAC address using OOB
+     *     (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer -
+     *     when using NAN discovery use the alternative network specifier method -
+     *     {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}.
+     *
+     * @param role  The role of this device:
+     *              {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
+     *              {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
+     * @param peer  The MAC address of the peer's NAN discovery interface. On a RESPONDER this
+     *              value is used to gate the acceptance of a connection request from only that
+     *              peer. A RESPONDER may specified a null - indicating that it will accept
+     *              connection requests from any device.
+     * @param token An arbitrary token (message) to be used to match connection initiation request
+     *              to a responder setup. A RESPONDER is set up with a {@code token} which must
+     *              be matched by the token provided by the INITIATOR. A null token is permitted
+     *              on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
+     *              not the same as a null token and requires the peer token to be empty as well.
+     *
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to {@link
+     * android.net.ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
+     */
+    public String createNetworkSpecifier(@DataPathRole int role, @Nullable byte[] peer,
+            @Nullable byte[] token) {
+        if (VDBG) {
+            Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token);
+        }
+
+        int type;
+        if (token != null && peer != null) {
+            type = NETWORK_SPECIFIER_TYPE_2A;
+        } else if (token == null && peer != null) {
+            type = NETWORK_SPECIFIER_TYPE_2B;
+        } else if (token != null && peer == null) {
+            type = NETWORK_SPECIFIER_TYPE_2C;
+        } else { // both are null
+            type = NETWORK_SPECIFIER_TYPE_2D;
+        }
+
+        if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
+                && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+            throw new IllegalArgumentException(
+                    "createNetworkSpecifier: Invalid 'role' argument when creating a network "
+                            + "specifier");
+        }
+        if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+            if (peer == null || peer.length != 6) {
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid peer MAC address");
+            }
+            if (token == null) {
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
+            }
+        } else {
+            if (peer != null && peer.length != 6) {
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid peer MAC address");
+            }
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mClientId == INVALID_CLIENT_ID) {
+                Log.e(TAG,
+                        "createNetworkSpecifier: called with invalid client ID - not connected "
+                                + "first?");
+                return null;
+            }
+
+            clientId = mClientId;
+        }
+
+        JSONObject json;
+        try {
+            json = new JSONObject();
+            json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
+            json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
+            json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
+            if (peer != null) {
+                json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer)));
+            }
+            if (token != null) {
+                json.put(NETWORK_SPECIFIER_KEY_TOKEN,
+                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+            }
+        } catch (JSONException e) {
+            return "";
+        }
+
+        return json.toString();
+    }
+
+    private static class WifiNanEventCallbackProxy extends IWifiNanEventCallback.Stub {
+        private static final int CALLBACK_CONNECT_SUCCESS = 0;
+        private static final int CALLBACK_CONNECT_FAIL = 1;
+        private static final int CALLBACK_IDENTITY_CHANGED = 2;
+        private static final int CALLBACK_RANGING_SUCCESS = 3;
+        private static final int CALLBACK_RANGING_FAILURE = 4;
+        private static final int CALLBACK_RANGING_ABORTED = 5;
+
+        private final Handler mHandler;
+        private final WeakReference<WifiNanManager> mNanManager;
+
+        RttManager.RttListener getAndRemoveRangingListener(int rangingId) {
+            WifiNanManager mgr = mNanManager.get();
+            if (mgr == null) {
+                Log.w(TAG, "getAndRemoveRangingListener: called post GC");
+                return null;
+            }
+
+            synchronized (mgr.mLock) {
+                RttManager.RttListener listener = mgr.mRangingListeners.get(rangingId);
+                mgr.mRangingListeners.delete(rangingId);
+                return listener;
+            }
+        }
+
+        /**
+         * Constructs a {@link WifiNanEventCallback} using the specified looper.
+         * All callbacks will delivered on the thread of the specified looper.
+         *
+         * @param looper The looper on which to execute the callbacks.
+         */
+        WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper,
+                final WifiNanEventCallback originalCallback) {
+            mNanManager = new WeakReference<>(mgr);
+
+            if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper);
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    if (DBG) {
+                        Log.d(TAG, "WifiNanEventCallbackProxy: What=" + msg.what + ", msg=" + msg);
+                    }
+
+                    WifiNanManager mgr = mNanManager.get();
+                    if (mgr == null) {
+                        Log.w(TAG, "WifiNanEventCallbackProxy: handleMessage post GC");
+                        return;
+                    }
+
+                    switch (msg.what) {
+                        case CALLBACK_CONNECT_SUCCESS:
+                            originalCallback.onConnectSuccess();
+                            break;
+                        case CALLBACK_CONNECT_FAIL:
+                            synchronized (mgr.mLock) {
+                                mgr.mLooper = null;
+                                mgr.mClientId = INVALID_CLIENT_ID;
+                            }
+                            mNanManager.clear();
+                            originalCallback.onConnectFail(msg.arg1);
+                            break;
+                        case CALLBACK_IDENTITY_CHANGED:
+                            originalCallback.onIdentityChanged((byte[]) msg.obj);
+                            break;
+                        case CALLBACK_RANGING_SUCCESS: {
+                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
+                            if (listener == null) {
+                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
+                                        + ": no listener registered (anymore)");
+                            } else {
+                                listener.onSuccess(
+                                        ((RttManager.ParcelableRttResults) msg.obj).mResults);
+                            }
+                            break;
+                        }
+                        case CALLBACK_RANGING_FAILURE: {
+                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
+                            if (listener == null) {
+                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
+                                        + ": no listener registered (anymore)");
+                            } else {
+                                listener.onFailure(msg.arg2, (String) msg.obj);
+                            }
+                            break;
+                        }
+                        case CALLBACK_RANGING_ABORTED: {
+                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
+                            if (listener == null) {
+                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
+                                        + ": no listener registered (anymore)");
+                            } else {
+                                listener.onAborted();
+                            }
+                            break;
+                        }
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void onConnectSuccess() {
+            if (VDBG) Log.v(TAG, "onConnectSuccess");
+
+            Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_SUCCESS);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onConnectFail(int reason) {
+            if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_FAIL);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onIdentityChanged(byte[] mac) {
+            if (VDBG) Log.v(TAG, "onIdentityChanged: mac=" + new String(HexEncoding.encode(mac)));
+
+            Message msg = mHandler.obtainMessage(CALLBACK_IDENTITY_CHANGED);
+            msg.obj = mac;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
+            if (VDBG) {
+                Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
+            }
+
+            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_SUCCESS);
+            msg.arg1 = rangingId;
+            msg.obj = results;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onRangingFailure(int rangingId, int reason, String description) {
+            if (VDBG) {
+                Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
+                        + ", description=" + description);
+            }
+
+            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_FAILURE);
+            msg.arg1 = rangingId;
+            msg.arg2 = reason;
+            msg.obj = description;
+            mHandler.sendMessage(msg);
+
+        }
+
+        @Override
+        public void onRangingAborted(int rangingId) {
+            if (VDBG) Log.v(TAG, "onRangingAborted: rangingId=" + rangingId);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_ABORTED);
+            msg.arg1 = rangingId;
+            mHandler.sendMessage(msg);
+
+        }
+    }
+
+    private static class WifiNanSessionCallbackProxy extends IWifiNanSessionCallback.Stub {
+        private static final int CALLBACK_SESSION_STARTED = 0;
+        private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1;
+        private static final int CALLBACK_SESSION_CONFIG_FAIL = 2;
+        private static final int CALLBACK_SESSION_TERMINATED = 3;
+        private static final int CALLBACK_MATCH = 4;
+        private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
+        private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
+        private static final int CALLBACK_MESSAGE_RECEIVED = 7;
+
+        private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
+        private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
+
+        private final WeakReference<WifiNanManager> mNanManager;
+        private final boolean mIsPublish;
+        private final WifiNanSessionCallback mOriginalCallback;
+
+        private final Handler mHandler;
+        private WifiNanSession mSession;
+
+        WifiNanSessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish,
+                WifiNanSessionCallback originalCallback) {
+            mNanManager = new WeakReference<>(mgr);
+            mIsPublish = isPublish;
+            mOriginalCallback = originalCallback;
+
+            if (VDBG) Log.v(TAG, "WifiNanSessionCallbackProxy ctor: isPublish=" + isPublish);
+
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+
+                    if (mNanManager.get() == null) {
+                        Log.w(TAG, "WifiNanSessionCallbackProxy: handleMessage post GC");
+                        return;
+                    }
+
+                    switch (msg.what) {
+                        case CALLBACK_SESSION_STARTED:
+                            onProxySessionStarted(msg.arg1);
+                            break;
+                        case CALLBACK_SESSION_CONFIG_SUCCESS:
+                            mOriginalCallback.onSessionConfigSuccess();
+                            break;
+                        case CALLBACK_SESSION_CONFIG_FAIL:
+                            mOriginalCallback.onSessionConfigFail(msg.arg1);
+                            if (mSession == null) {
+                                /*
+                                 * creation failed (as opposed to update
+                                 * failing)
+                                 */
+                                mNanManager.clear();
+                            }
+                            break;
+                        case CALLBACK_SESSION_TERMINATED:
+                            onProxySessionTerminated(msg.arg1);
+                            break;
+                        case CALLBACK_MATCH:
+                            mOriginalCallback.onMatch(
+                                    msg.arg1,
+                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2));
+                            break;
+                        case CALLBACK_MESSAGE_SEND_SUCCESS:
+                            mOriginalCallback.onMessageSendSuccess(msg.arg1);
+                            break;
+                        case CALLBACK_MESSAGE_SEND_FAIL:
+                            mOriginalCallback.onMessageSendFail(msg.arg1, msg.arg2);
+                            break;
+                        case CALLBACK_MESSAGE_RECEIVED:
+                            mOriginalCallback.onMessageReceived(msg.arg1, (byte[]) msg.obj);
+                            break;
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void onSessionStarted(int sessionId) {
+            if (VDBG) Log.v(TAG, "onSessionStarted: sessionId=" + sessionId);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_SESSION_STARTED);
+            msg.arg1 = sessionId;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onSessionConfigSuccess() {
+            if (VDBG) Log.v(TAG, "onSessionConfigSuccess");
+
+            Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_SUCCESS);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onSessionConfigFail(int reason) {
+            if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_FAIL);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onSessionTerminated(int reason) {
+            if (VDBG) Log.v(TAG, "onSessionTerminated: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_SESSION_TERMINATED);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+            Bundle data = new Bundle();
+            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
+            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
+            msg.arg1 = peerId;
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageSendSuccess(int messageId) {
+            if (VDBG) Log.v(TAG, "onMessageSendSuccess");
+
+            Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_SUCCESS);
+            msg.arg1 = messageId;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageSendFail(int messageId, int reason) {
+            if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_FAIL);
+            msg.arg1 = messageId;
+            msg.arg2 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageReceived(int peerId, byte[] message) {
+            if (VDBG) {
+                Log.v(TAG, "onMessageReceived: peerId='" + peerId);
+            }
+
+            Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED);
+            msg.arg1 = peerId;
+            msg.obj = message;
+            mHandler.sendMessage(msg);
+        }
+
+        /*
+         * Proxied methods
+         */
+        public void onProxySessionStarted(int sessionId) {
+            if (VDBG) Log.v(TAG, "Proxy: onSessionStarted: sessionId=" + sessionId);
+            if (mSession != null) {
+                Log.e(TAG,
+                        "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
+                throw new IllegalStateException(
+                        "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
+            }
+
+            WifiNanManager mgr = mNanManager.get();
+            if (mgr == null) {
+                Log.w(TAG, "onProxySessionStarted: mgr GC'd");
+                return;
+            }
+
+            if (mIsPublish) {
+                WifiNanPublishSession session = new WifiNanPublishSession(mgr, sessionId);
+                mSession = session;
+                mOriginalCallback.onPublishStarted(session);
+            } else {
+                WifiNanSubscribeSession session = new WifiNanSubscribeSession(mgr, sessionId);
+                mSession = session;
+                mOriginalCallback.onSubscribeStarted(session);
+            }
+        }
+
+        public void onProxySessionTerminated(int reason) {
+            if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason);
+            if (mSession != null) {
+                mSession.setTerminated();
+                mSession = null;
+            } else {
+                Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
+            }
+            mNanManager.clear();
+            mOriginalCallback.onSessionTerminated(reason);
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
index 81b38f4..ccd7fae 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
@@ -16,32 +16,54 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.NonNull;
+import android.util.Log;
+
 /**
- * A representation of a NAN publish session. Created when
- * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
- * is executed. The object can be used to stop and re-start (re-configure) the
- * publish session.
+ * A class representing a NAN publish session. Created when
+ * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} is called and a
+ * discovery session is created and returned in
+ * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)}. See baseline
+ * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating
+ * an existing/running publish discovery session using {@link #updatePublish(PublishConfig)}.
  *
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanPublishSession extends WifiNanSession {
-    /**
-     * {@hide}
-     */
+    private static final String TAG = "WifiNanPublishSession";
+
+    /** @hide */
     public WifiNanPublishSession(WifiNanManager manager, int sessionId) {
         super(manager, sessionId);
     }
 
     /**
-     * Restart/re-configure the publish session. Note that the
-     * {@link WifiNanSessionListener} is not replaced - the same listener used at
-     * creation is still used.
+     * Re-configure the currently active publish session. The
+     * {@link WifiNanSessionCallback} is not replaced - the same listener used
+     * at creation is still used. The results of the configuration are returned using
+     * {@link WifiNanSessionCallback}:
+     * <ul>
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update
+     *     succeeded.
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update
+     *     failed. The publish discovery session is still running using its previous
+     *     configuration (i.e. update failure does not terminate the session).
+     * </ul>
      *
-     * @param publishData The data ({@link PublishData}) to publish.
-     * @param publishSettings The settings ({@link PublishSettings}) of the
-     *            publish session.
+     * @param publishConfig The new discovery publish session configuration ({@link PublishConfig}).
      */
-    public void publish(PublishData publishData, PublishSettings publishSettings) {
-        mManager.publish(mSessionId, publishData, publishSettings);
+    public void updatePublish(@NonNull PublishConfig publishConfig) {
+        if (mTerminated) {
+            Log.w(TAG, "updatePublish: called on terminated session");
+            return;
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "updatePublish: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.updatePublish(mSessionId, publishConfig);
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index bc1787f..005ca29 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -16,12 +16,28 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.wifi.RttManager;
 import android.util.Log;
 
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
+
 /**
- * A representation of a single publish or subscribe NAN session. This object
+ * A class representing a single publish or subscribe NAN session. This object
  * will not be created directly - only its child classes are available:
- * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}.
+ * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}. This class provides
+ * functionality common to both publish and subscribe discovery sessions:
+ * <ul>
+ *     <li>Sending messages: {@link #sendMessage(int, byte[], int)} or
+ *     {@link #sendMessage(int, byte[], int, int)} methods.
+ *     <li>Creating a network-specifier when requesting a NAN connection:
+ *     {@link #createNetworkSpecifier(int, int, byte[])}.
+ * </ul>
+ * The {@link #terminate()} method must be called to terminate discovery sessions once they are
+ * no longer needed.
  *
  * @hide PROPOSED_NAN_API
  */
@@ -30,84 +46,234 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    /**
-     * {@hide}
-     */
-    protected WifiNanManager mManager;
+    private static final int MAX_SEND_RETRY_COUNT = 5;
+
+    /** @hide */
+    protected WeakReference<WifiNanManager> mMgr;
+    /** @hide */
+    protected final int mSessionId;
+    /** @hide */
+    protected boolean mTerminated = false;
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
     /**
-     * {@hide}
+     * Return the maximum permitted retry count when sending messages using
+     * {@link #sendMessage(int, byte[], int, int)}.
+     *
+     * @return Maximum retry count when sending messages.
      */
-    protected int mSessionId;
+    public static int getMaxSendRetryCount() {
+        return MAX_SEND_RETRY_COUNT;
+    }
 
-    /**
-     * {@hide}
-     */
-    private boolean mDestroyed;
-
-    /**
-     * {@hide}
-     */
+    /** @hide */
     public WifiNanSession(WifiNanManager manager, int sessionId) {
         if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId);
 
-        mManager = manager;
+        mMgr = new WeakReference<>(manager);
         mSessionId = sessionId;
-        mDestroyed = false;
+
+        mCloseGuard.open("terminate");
     }
 
     /**
-     * Terminate the current publish or subscribe session - i.e. stop
-     * transmitting packet on-air (for an active session) or listening for
-     * matches (for a passive session). Note that the session may still receive
-     * incoming messages and may be re-configured/re-started at a later time.
+     * Terminate the publish or subscribe session - free any resources, and stop
+     * transmitting packets on-air (for an active session) or listening for
+     * matches (for a passive session). The session may not be used for any
+     * additional operations after termination.
+     * <p>
+     *     This operation must be done on a session which is no longer needed. Otherwise system
+     *     resources will continue to be utilized until the application terminates. The only
+     *     exception is a session for which we received a termination callback,
+     *     {@link WifiNanSessionCallback#onSessionTerminated(int)}.
      */
-    public void stop() {
-        mManager.stopSession(mSessionId);
+    public void terminate() {
+        WifiNanManager mgr = mMgr.get();
+        if (mgr == null) {
+            Log.w(TAG, "terminate: called post GC on WifiNanManager");
+            return;
+        }
+        mgr.terminateSession(mSessionId);
+        mTerminated = true;
+        mMgr.clear();
+        mCloseGuard.close();
     }
 
     /**
-     * Destroy the current publish or subscribe session. Performs a
-     * {@link WifiNanSession#stop()} function but in addition destroys the session -
-     * it will not be able to receive any messages or to be restarted at a later
-     * time.
+     * Sets the status of the session to terminated - i.e. an indication that
+     * already terminated rather than executing a termination.
+     *
+     * @hide
      */
-    public void destroy() {
-        mManager.destroySession(mSessionId);
-        mDestroyed = true;
+    public void setTerminated() {
+        if (mTerminated) {
+            Log.w(TAG, "terminate: already terminated.");
+            return;
+        }
+        mTerminated = true;
+        mMgr.clear();
+        mCloseGuard.close();
     }
 
-    /**
-     * {@hide}
-     */
+    /** @hide */
     @Override
     protected void finalize() throws Throwable {
-        if (!mDestroyed) {
-            Log.w(TAG, "WifiNanSession mSessionId=" + mSessionId
-                            + " was not explicitly destroyed. The session may use resources until "
-                            + "destroyed so step should be done explicitly");
+        try {
+            if (!mTerminated) {
+                mCloseGuard.warnIfOpen();
+                terminate();
+            }
+        } finally {
+            super.finalize();
         }
-        destroy();
     }
 
     /**
-     * Sends a message to the specified destination. Message transmission is
-     * part of the current discovery session - i.e. executed subsequent to a
-     * publish/subscribe
-     * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
-     * event.
+     * Sends a message to the specified destination. NAN messages are transmitted in the context
+     * of a discovery session - executed subsequent to a publish/subscribe
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event.
+     * <p>
+     *     NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback}
+     *     indicate message was transmitted successfully,
+     *     {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed
+     *     (possibly after several retries) -
+     *     {@link WifiNanSessionCallback#onMessageSendFail(int, int)}.
+     * <p>
+     *     The peer will get a callback indicating a message was received using
+     *     {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}.
      *
      * @param peerId The peer's ID for the message. Must be a result of an
-     *            {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
-     *            event.
+     *            {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     *            {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events.
      * @param message The message to be transmitted.
-     * @param messageLength The number of bytes from the {@code message} to be
-     *            transmitted.
-     * @param messageId An arbitrary integer used by the caller to identify the
-     *            message. The same integer ID will be returned in the callbacks
-     *            indicated message send success or failure.
+     * @param messageId An arbitrary integer used by the caller to identify the message. The same
+     *            integer ID will be returned in the callbacks indicating message send success or
+     *            failure. The {@code messageId} is not used internally by the NAN service - it
+     *                  can be arbitrary and non-unique.
+     * @param retryCount An integer specifying how many additional service-level (as opposed to PHY
+     *            or MAC level) retries should be attempted if there is no ACK from the receiver
+     *            (note: no retransmissions are attempted in other failure cases). A value of 0
+     *            indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
      */
-    public void sendMessage(int peerId, byte[] message, int messageLength, int messageId) {
-        mManager.sendMessage(mSessionId, peerId, message, messageLength, messageId);
+    public void sendMessage(int peerId, @Nullable byte[] message, int messageId, int retryCount) {
+        if (mTerminated) {
+            Log.w(TAG, "sendMessage: called on terminated session");
+            return;
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "sendMessage: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.sendMessage(mSessionId, peerId, message, messageId, retryCount);
+        }
+    }
+
+    /**
+     * Sends a message to the specified destination. NAN messages are transmitted in the context
+     * of a discovery session - executed subsequent to a publish/subscribe
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event.
+     * <p>
+     *     NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback}
+     *     indicate message was transmitted successfully,
+     *     {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed
+     *     (possibly after several retries) -
+     *     {@link WifiNanSessionCallback#onMessageSendFail(int, int)}.
+     * <p>
+     *     The peer will get a callback indicating a message was received using
+     *     {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}.
+     * Equivalent to {@link #sendMessage(int, byte[], int, int)} with a {@code retryCount} of
+     * 0.
+     *
+     * @param peerId The peer's ID for the message. Must be a result of an
+     *            {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     *            {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events.
+     * @param message The message to be transmitted.
+     * @param messageId An arbitrary integer used by the caller to identify the message. The same
+     *            integer ID will be returned in the callbacks indicating message send success or
+     *            failure. The {@code messageId} is not used internally by the NAN service - it
+     *                  can be arbitrary and non-unique.
+     */
+    public void sendMessage(int peerId, @Nullable byte[] message, int messageId) {
+        sendMessage(peerId, message, messageId, 0);
+    }
+
+    /**
+     * Start a ranging operation with the specified peers. The peer IDs are obtained from an
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} operation - can only
+     * range devices which are part of an ongoing discovery session.
+     *
+     * @param params   RTT parameters - each corresponding to a specific peer ID (the array sizes
+     *                 must be identical). The
+     *                 {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
+     *                 a peer ID - not to a MAC address.
+     * @param listener The listener to receive the results of the ranging session.
+     * @hide PROPOSED_NAN_SYSTEM_API [TODO: b/28847998 - track RTT API & visilibity]
+     */
+    public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
+        if (mTerminated) {
+            Log.w(TAG, "startRanging: called on terminated session");
+            return;
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "startRanging: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.startRanging(mSessionId, params, listener);
+        }
+    }
+
+    /**
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
+     * WiFi NAN connection to the specified peer. The
+     * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+     * <p>
+     * This method should be used when setting up a connection with a peer discovered through NAN
+     * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+     * an opaque peer ID handle). If a NAN connection is needed to a peer discovered using other
+     * OOB (out-of-band) mechanism then use the alternative
+     * {@link WifiNanManager#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
+     * peer's MAC address.
+     *
+     * @param role The role of this device:
+     * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
+     * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
+     * @param peerId The peer ID obtained through
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. On a RESPONDER this
+     *              value is used to gate the acceptance of a connection request from only that
+     *              peer. A RESPONDER may specified a 0 - indicating that it will accept
+     *              connection requests from any device.
+     * @param token An arbitrary token (message) to be used to match connection initiation request
+     *              to a responder setup. A RESPONDER is set up with a {@code token} which must
+     *              be matched by the token provided by the INITIATOR. A null token is permitted
+     *              on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
+     *              not the same as a null token and requires the peer token to be empty as well.
+     *
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,android.net.ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
+     */
+    public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, int peerId,
+            @Nullable byte[] token) {
+        if (mTerminated) {
+            Log.w(TAG, "createNetworkSpecifier: called on terminated session");
+            return null;
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "createNetworkSpecifier: called post GC on WifiNanManager");
+                return null;
+            }
+
+            return mgr.createNetworkSpecifier(role, mSessionId, peerId, token);
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java b/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
new file mode 100644
index 0000000..8433b99
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for NAN session events callbacks. Should be extended by
+ * applications wanting notifications. The callbacks are set when a
+ * publish or subscribe session is created using
+ * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or
+ * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} .
+ * <p>
+ * A single callback is set at session creation - it cannot be replaced.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSessionCallback {
+    /** @hide */
+    @IntDef({
+            REASON_NO_RESOURCES, REASON_INVALID_ARGS, REASON_NO_MATCH_SESSION,
+            REASON_TX_FAIL, REASON_OTHER })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionReasonCodes {
+    }
+
+    /** @hide */
+    @IntDef({
+            TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionTerminateCodes {
+    }
+
+    /**
+     * Indicates no resources to execute the requested operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     */
+    public static final int REASON_NO_RESOURCES = 0;
+
+    /**
+     * Indicates invalid argument in the requested operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     */
+    public static final int REASON_INVALID_ARGS = 1;
+
+    /**
+     * Indicates a message is transmitted without a match (a discovery) or received message
+     * from peer occurring first.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     */
+    public static final int REASON_NO_MATCH_SESSION = 2;
+
+    /**
+     * Indicates transmission failure: this may be due to local transmission
+     * failure or to no ACK received - remote device didn't receive the
+     * sent message. Failure reason flag for
+     * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} callback.
+     */
+    public static final int REASON_TX_FAIL = 3;
+
+    /**
+     * Indicates an unspecified error occurred during the operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     */
+    public static final int REASON_OTHER = 4;
+
+    /**
+     * Indicates that publish or subscribe session is done - all the
+     * requested operations (per {@link PublishConfig} or
+     * {@link SubscribeConfig}) have been executed. Failure reason flag for
+     * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback.
+     */
+    public static final int TERMINATE_REASON_DONE = 100;
+
+    /**
+     * Indicates that publish or subscribe session is terminated due to a
+     * failure.
+     * Failure reason flag for
+     * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback.
+     */
+    public static final int TERMINATE_REASON_FAIL = 101;
+
+    /**
+     * Called when a publish operation is started successfully in response to a
+     * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} operation.
+     *
+     * @param session The {@link WifiNanPublishSession} used to control the
+     *            discovery session.
+     */
+    public void onPublishStarted(@NonNull WifiNanPublishSession session) {
+        /* empty */
+    }
+
+    /**
+     * Called when a subscribe operation is started successfully in response to a
+     * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} operation.
+     *
+     * @param session The {@link WifiNanSubscribeSession} used to control the
+     *            discovery session.
+     */
+    public void onSubscribeStarted(@NonNull WifiNanSubscribeSession session) {
+        /* empty */
+    }
+
+    /**
+     * Called when a publish or subscribe discovery session configuration is update request
+     * succeeds. Called in response to {@link WifiNanPublishSession#updatePublish(PublishConfig)}
+     * or {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}.
+     */
+    public void onSessionConfigSuccess() {
+        /* empty */
+    }
+
+    /**
+     * Called when a publish or subscribe discovery session cannot be created:
+     * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or
+     * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)},
+     * or when a configuration update fails:
+     * {@link WifiNanPublishSession#updatePublish(PublishConfig)} or
+     * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}.
+     * <p>
+     *     For discovery session updates failure leaves the session running with its previous
+     *     configuration - the discovery session is not terminated.
+     *
+     * @param reason The failure reason using
+     *            {@code WifiNanSessionCallback.REASON_*} codes.
+     */
+    public void onSessionConfigFail(@SessionReasonCodes int reason) {
+        /* empty */
+    }
+
+    /**
+     * Called when a discovery session (publish or subscribe) terminates. Termination may be due
+     * to user-request (either directly through {@link WifiNanSession#terminate()} or
+     * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
+     * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure.
+     *
+     * @param reason The termination reason using
+     *            {@code WifiNanSessionCallback.TERMINATE_*} codes.
+     */
+    public void onSessionTerminated(@SessionTerminateCodes int reason) {
+        /* empty */
+    }
+
+    /**
+     * Called when a discovery (publish or subscribe) operation results in a
+     * match - when a peer is discovered.
+     *
+     * @param peerId The ID of the peer matching our discovery operation.
+     * @param serviceSpecificInfo The service specific information (arbitrary
+     *            byte array) provided by the peer as part of its discovery
+     *            configuration.
+     * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
+     *            resulted in this match.
+     */
+    public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+        /* empty */
+    }
+
+    /**
+     * Called in response to {@link WifiNanSession#sendMessage(int, byte[], int)} when a message
+     * is transmitted successfully - when it was received successfully by the peer
+     * (corresponds to an ACK being received).
+     * <p>
+     * Note that either this callback or
+     * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} will be
+     * received - never both.
+     *
+     * @param messageId The arbitrary message ID specified when sending the message.
+     */
+    public void onMessageSendSuccess(@SuppressWarnings("unused") int messageId) {
+        /* empty */
+    }
+
+    /**
+     * Called when message transmission fails - when no ACK is received from the peer.
+     * Retries when ACKs are not received are done by hardware, MAC, and in the NAN stack (using
+     * the {@link WifiNanSession#sendMessage(int, byte[], int, int)} method) - this
+     * event is received after all retries are exhausted.
+     * <p>
+     * Note that either this callback or
+     * {@link WifiNanSessionCallback#onMessageSendSuccess(int)} will be received
+     * - never both.
+     *
+     * @param messageId The arbitrary message ID specified when sending the message.
+     * @param reason The failure reason using
+     *            {@code WifiNanSessionCallback.REASON_*} codes.
+     */
+    public void onMessageSendFail(@SuppressWarnings("unused") int messageId,
+            @SessionReasonCodes int reason) {
+        /* empty */
+    }
+
+    /**
+     * Called when a message is received from a discovery session peer - in response to the
+     * peer's {@link WifiNanSession#sendMessage(int, byte[], int)} or
+     * {@link WifiNanSession#sendMessage(int, byte[], int, int)}.
+     *
+     * @param peerId The ID of the peer sending the message.
+     * @param message A byte array containing the message.
+     */
+    public void onMessageReceived(int peerId, byte[] message) {
+        /* empty */
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
deleted file mode 100644
index b9af7def..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2016 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.net.wifi.nan;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * Base class for NAN session events callbacks. Should be extended by
- * applications wanting notifications. The callbacks are registered when a
- * publish or subscribe session is created using
- * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
- * or
- * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
- * . These are callbacks applying to a specific NAN session. Events
- * corresponding to the NAN link are delivered using {@link WifiNanEventListener}.
- * <p>
- * A single listener is registered at session creation - it cannot be replaced.
- * <p>
- * During registration specify which specific events are desired using a set of
- * {@code NanSessionListener.LISTEN_*} flags OR'd together. Only those events
- * will be delivered to the registered listener. Override those callbacks
- * {@code NanSessionListener.on*} for the registered events.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanSessionListener {
-    private static final String TAG = "WifiNanSessionListener";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false; // STOPSHIP if true
-
-    /**
-     * Publish fail callback event registration flag. Corresponding callback is
-     * {@link WifiNanSessionListener#onPublishFail(int)}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_PUBLISH_FAIL = 0x1 << 0;
-
-    /**
-     * Publish terminated callback event registration flag. Corresponding
-     * callback is {@link WifiNanSessionListener#onPublishTerminated(int)}.
-     */
-    public static final int LISTEN_PUBLISH_TERMINATED = 0x1 << 1;
-
-    /**
-     * Subscribe fail callback event registration flag. Corresponding callback
-     * is {@link WifiNanSessionListener#onSubscribeFail(int)}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_SUBSCRIBE_FAIL = 0x1 << 2;
-
-    /**
-     * Subscribe terminated callback event registration flag. Corresponding
-     * callback is {@link WifiNanSessionListener#onSubscribeTerminated(int)}.
-     */
-    public static final int LISTEN_SUBSCRIBE_TERMINATED = 0x1 << 3;
-
-    /**
-     * Match (discovery: publish or subscribe) callback event registration flag.
-     * Corresponding callback is
-     * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_MATCH = 0x1 << 4;
-
-    /**
-     * Message sent successfully callback event registration flag. Corresponding
-     * callback is {@link WifiNanSessionListener#onMessageSendSuccess()}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_MESSAGE_SEND_SUCCESS = 0x1 << 5;
-
-    /**
-     * Message sending failure callback event registration flag. Corresponding
-     * callback is {@link WifiNanSessionListener#onMessageSendFail(int)}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_MESSAGE_SEND_FAIL = 0x1 << 6;
-
-    /**
-     * Message received callback event registration flag. Corresponding callback
-     * is {@link WifiNanSessionListener#onMessageReceived(int, byte[], int)}.
-     *
-     * @hide
-     */
-    public static final int LISTEN_MESSAGE_RECEIVED = 0x1 << 7;
-
-    /**
-     * List of hidden events: which are mandatory - i.e. they will be added to
-     * every request.
-     *
-     * @hide
-     */
-    public static final int LISTEN_HIDDEN_FLAGS = LISTEN_PUBLISH_FAIL | LISTEN_SUBSCRIBE_FAIL
-            | LISTEN_MATCH | LISTEN_MESSAGE_SEND_SUCCESS | LISTEN_MESSAGE_SEND_FAIL
-            | LISTEN_MESSAGE_RECEIVED;
-
-    /**
-     * Failure reason flag for {@link WifiNanEventListener} and
-     * {@link WifiNanSessionListener} callbacks. Indicates no resources to execute
-     * the requested operation.
-     */
-    public static final int FAIL_REASON_NO_RESOURCES = 0;
-
-    /**
-     * Failure reason flag for {@link WifiNanEventListener} and
-     * {@link WifiNanSessionListener} callbacks. Indicates invalid argument in the
-     * requested operation.
-     */
-    public static final int FAIL_REASON_INVALID_ARGS = 1;
-
-    /**
-     * Failure reason flag for {@link WifiNanEventListener} and
-     * {@link WifiNanSessionListener} callbacks. Indicates a message is transmitted
-     * without a match (i.e. a discovery) occurring first.
-     */
-    public static final int FAIL_REASON_NO_MATCH_SESSION = 2;
-
-    /**
-     * Failure reason flag for {@link WifiNanEventListener} and
-     * {@link WifiNanSessionListener} callbacks. Indicates an unspecified error
-     * occurred during the operation.
-     */
-    public static final int FAIL_REASON_OTHER = 3;
-
-    /**
-     * Failure reason flag for
-     * {@link WifiNanSessionListener#onPublishTerminated(int)} and
-     * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
-     * Indicates that publish or subscribe session is done - i.e. all the
-     * requested operations (per {@link PublishSettings} or
-     * {@link SubscribeSettings}) have been executed.
-     */
-    public static final int TERMINATE_REASON_DONE = 0;
-
-    /**
-     * Failure reason flag for
-     * {@link WifiNanSessionListener#onPublishTerminated(int)} and
-     * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
-     * Indicates that publish or subscribe session is terminated due to a
-     * failure.
-     */
-    public static final int TERMINATE_REASON_FAIL = 1;
-
-    private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
-    private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
-    private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
-
-    private final Handler mHandler;
-
-    /**
-     * Constructs a {@link WifiNanSessionListener} using the looper of the current
-     * thread. I.e. all callbacks will be delivered on the current thread.
-     */
-    public WifiNanSessionListener() {
-        this(Looper.myLooper());
-    }
-
-    /**
-     * Constructs a {@link WifiNanSessionListener} using the specified looper. I.e.
-     * all callbacks will delivered on the thread of the specified looper.
-     *
-     * @param looper The looper on which to execute the callbacks.
-     */
-    public WifiNanSessionListener(Looper looper) {
-        if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
-        mHandler = new Handler(looper) {
-            @Override
-            public void handleMessage(Message msg) {
-                if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
-                switch (msg.what) {
-                    case LISTEN_PUBLISH_FAIL:
-                        WifiNanSessionListener.this.onPublishFail(msg.arg1);
-                        break;
-                    case LISTEN_PUBLISH_TERMINATED:
-                        WifiNanSessionListener.this.onPublishTerminated(msg.arg1);
-                        break;
-                    case LISTEN_SUBSCRIBE_FAIL:
-                        WifiNanSessionListener.this.onSubscribeFail(msg.arg1);
-                        break;
-                    case LISTEN_SUBSCRIBE_TERMINATED:
-                        WifiNanSessionListener.this.onSubscribeTerminated(msg.arg1);
-                        break;
-                    case LISTEN_MATCH:
-                        WifiNanSessionListener.this.onMatch(
-                                msg.getData().getInt(MESSAGE_BUNDLE_KEY_PEER_ID),
-                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1,
-                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2);
-                        break;
-                    case LISTEN_MESSAGE_SEND_SUCCESS:
-                        WifiNanSessionListener.this.onMessageSendSuccess(msg.arg1);
-                        break;
-                    case LISTEN_MESSAGE_SEND_FAIL:
-                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1, msg.arg2);
-                        break;
-                    case LISTEN_MESSAGE_RECEIVED:
-                        WifiNanSessionListener.this.onMessageReceived(msg.arg2,
-                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1);
-                        break;
-                }
-            }
-        };
-    }
-
-    /**
-     * Called when a publish operation fails. It is dummy method (empty
-     * implementation printing out a log message). Override to implement your
-     * custom response.
-     *
-     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
-     *            codes.
-     */
-    public void onPublishFail(int reason) {
-        if (VDBG) Log.v(TAG, "onPublishFail: called in stub - override if interested");
-    }
-
-    /**
-     * Called when a publish operation terminates. Event will only be delivered
-     * if registered using {@link WifiNanSessionListener#LISTEN_PUBLISH_TERMINATED}.
-     * A dummy (empty implementation printing out a warning). Make sure to
-     * override if registered.
-     *
-     * @param reason The termination reason using
-     *            {@code NanSessionListener.TERMINATE_*} codes.
-     */
-    public void onPublishTerminated(int reason) {
-        Log.w(TAG, "onPublishTerminated: called in stub - override if interested or disable");
-    }
-
-    /**
-     * Called when a subscribe operation fails. It is dummy method (empty
-     * implementation printing out a log message). Override to implement your
-     * custom response.
-     *
-     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
-     *            codes.
-     */
-    public void onSubscribeFail(int reason) {
-        if (VDBG) Log.v(TAG, "onSubscribeFail: called in stub - override if interested");
-    }
-
-    /**
-     * Called when a subscribe operation terminates. Event will only be
-     * delivered if registered using
-     * {@link WifiNanSessionListener#LISTEN_SUBSCRIBE_TERMINATED}. A dummy (empty
-     * implementation printing out a warning). Make sure to override if
-     * registered.
-     *
-     * @param reason The termination reason using
-     *            {@code NanSessionListener.TERMINATE_*} codes.
-     */
-    public void onSubscribeTerminated(int reason) {
-        Log.w(TAG, "onSubscribeTerminated: called in stub - override if interested or disable");
-    }
-
-    /**
-     * Called when a discovery (publish or subscribe) operation results in a
-     * match - i.e. when a peer is discovered. It is dummy method (empty
-     * implementation printing out a log message). Override to implement your
-     * custom response.
-     *
-     * @param peerId The ID of the peer matching our discovery operation.
-     * @param serviceSpecificInfo The service specific information (arbitrary
-     *            byte array) provided by the peer as part of its discovery
-     *            packet.
-     * @param serviceSpecificInfoLength The length of the service specific
-     *            information array.
-     * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
-     *            resulted in this match.
-     * @param matchFilterLength The length of the match filter array.
-     */
-    public void onMatch(int peerId, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
-        if (VDBG) Log.v(TAG, "onMatch: called in stub - override if interested");
-    }
-
-    /**
-     * Called when a message is transmitted successfully - i.e. when we know
-     * that it was received successfully (corresponding to an ACK being
-     * received). It is dummy method (empty implementation printing out a log
-     * message). Override to implement your custom response.
-     * <p>
-     * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendFail(int, int)} will be
-     * received - never both.
-     */
-    public void onMessageSendSuccess(int messageId) {
-        if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
-    }
-
-    /**
-     * Called when a message transmission fails - i.e. when no ACK is received.
-     * The hardware will usually attempt to re-transmit several times - this
-     * event is received after all retries are exhausted. There is a possibility
-     * that message was received by the destination successfully but the ACK was
-     * lost. It is dummy method (empty implementation printing out a log
-     * message). Override to implement your custom response.
-     * <p>
-     * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendSuccess(int)} will be received
-     * - never both
-     *
-     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
-     *            codes.
-     */
-    public void onMessageSendFail(int messageId, int reason) {
-        if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested");
-    }
-
-    /**
-     * Called when a message is received from a discovery session peer. It is
-     * dummy method (empty implementation printing out a log message). Override
-     * to implement your custom response.
-     *
-     * @param peerId The ID of the peer sending the message.
-     * @param message A byte array containing the message.
-     * @param messageLength The length of the byte array containing the relevant
-     *            message bytes.
-     */
-    public void onMessageReceived(int peerId, byte[] message, int messageLength) {
-        if (VDBG) Log.v(TAG, "onMessageReceived: called in stub - override if interested");
-    }
-
-    /**
-     * {@hide}
-     */
-    public IWifiNanSessionListener callback = new IWifiNanSessionListener.Stub() {
-        @Override
-        public void onPublishFail(int reason) {
-            if (VDBG) Log.v(TAG, "onPublishFail: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_FAIL);
-            msg.arg1 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onPublishTerminated(int reason) {
-            if (VDBG) Log.v(TAG, "onPublishResponse: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_TERMINATED);
-            msg.arg1 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onSubscribeFail(int reason) {
-            if (VDBG) Log.v(TAG, "onSubscribeFail: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_FAIL);
-            msg.arg1 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onSubscribeTerminated(int reason) {
-            if (VDBG) Log.v(TAG, "onSubscribeTerminated: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_TERMINATED);
-            msg.arg1 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onMatch(int peerId, byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
-            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
-
-            Bundle data = new Bundle();
-            data.putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
-            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
-            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
-
-            Message msg = mHandler.obtainMessage(LISTEN_MATCH);
-            msg.arg1 = serviceSpecificInfoLength;
-            msg.arg2 = matchFilterLength;
-            msg.setData(data);
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onMessageSendSuccess(int messageId) {
-            if (VDBG) Log.v(TAG, "onMessageSendSuccess");
-
-            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS);
-            msg.arg1 = messageId;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onMessageSendFail(int messageId, int reason) {
-            if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
-
-            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL);
-            msg.arg1 = messageId;
-            msg.arg2 = reason;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onMessageReceived(int peerId, byte[] message, int messageLength) {
-            if (VDBG) {
-                Log.v(TAG, "onMessageReceived: peerId='" + peerId + "', messageLength="
-                        + messageLength);
-            }
-
-            Bundle data = new Bundle();
-            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
-
-            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_RECEIVED);
-            msg.arg1 = messageLength;
-            msg.arg2 = peerId;
-            msg.setData(data);
-            mHandler.sendMessage(msg);
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
index 7dfdd32..d0e56c5 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
@@ -16,15 +16,22 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.NonNull;
+import android.util.Log;
+
 /**
- * A representation of a NAN subscribe session. Created when
- * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
- * is executed. The object can be used to stop and re-start (re-configure) the
- * subscribe session.
+ * A class representing a NAN subscribe session. Created when
+ * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} is called and a
+ * discovery session is created and returned in
+ * {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)}. See baseline
+ * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating
+ * an existing/running subscribe discovery session using {@link #updateSubscribe(SubscribeConfig)}.
  *
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanSubscribeSession extends WifiNanSession {
+    private static final String TAG = "WifiNanSubscribeSession";
+
     /**
      * {@hide}
      */
@@ -33,15 +40,33 @@
     }
 
     /**
-     * Restart/re-configure the subscribe session. Note that the
-     * {@link WifiNanSessionListener} is not replaced - the same listener used at
-     * creation is still used.
+     * Re-configure the currently active subscribe session. The
+     * {@link WifiNanSessionCallback} is not replaced - the same listener used
+     * at creation is still used. The results of the configuration are returned using
+     * {@link WifiNanSessionCallback}:
+     * <ul>
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update
+     *     succeeded.
+     *     <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update
+     *     failed. The subscribe discovery session is still running using its previous
+     *     configuration (i.e. update failure does not terminate the session).
+     * </ul>
      *
-     * @param subscribeData The data ({@link SubscribeData}) to subscribe.
-     * @param subscribeSettings The settings ({@link SubscribeSettings}) of the
-     *            subscribe session.
+     * @param subscribeConfig The new discovery subscribe session configuration
+     *                        ({@link SubscribeConfig}).
      */
-    public void subscribe(SubscribeData subscribeData, SubscribeSettings subscribeSettings) {
-        mManager.subscribe(mSessionId, subscribeData, subscribeSettings);
+    public void updateSubscribe(@NonNull SubscribeConfig subscribeConfig) {
+        if (mTerminated) {
+            Log.w(TAG, "updateSubscribe: called on terminated session");
+            return;
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "updateSubscribe: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.updateSubscribe(mSessionId, subscribeConfig);
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanUtils.java b/wifi/java/android/net/wifi/nan/WifiNanUtils.java
new file mode 100644
index 0000000..c0f36b4
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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.net.wifi.nan;
+
+/**
+ * Provides utilities for the Wifi NAN manager/service.
+ *
+ * @hide
+ */
+public class WifiNanUtils {
+    /**
+     * Per spec: The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. The
+     * only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric values (A-Z,
+     * a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte UTF-8 characters
+     * are acceptable in a Service Name.
+     */
+    public static void validateServiceName(byte[] serviceNameData) throws IllegalArgumentException {
+        if (serviceNameData == null) {
+            throw new IllegalArgumentException("Invalid service name - null");
+        }
+
+        if (serviceNameData.length < 1 || serviceNameData.length > 255) {
+            throw new IllegalArgumentException("Invalid service name length - must be between "
+                    + "1 and 255 bytes (UTF-8 encoding)");
+        }
+
+        int index = 0;
+        while (index < serviceNameData.length) {
+            byte b = serviceNameData[index];
+            if ((b & 0x80) == 0x00) {
+                if (!((b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
+                        || b == '-' || b == '.')) {
+                    throw new IllegalArgumentException("Invalid service name - illegal characters,"
+                            + " allowed = (0-9, a-z,A-Z, -, .)");
+                }
+            }
+            ++index;
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/package.html b/wifi/java/android/net/wifi/nan/package.html
new file mode 100644
index 0000000..ae3cf6c
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/package.html
@@ -0,0 +1,43 @@
+<HTML>
+<BODY>
+<p>Provides classes which allow applications to use Wi-Fi NAN to discover peers and create
+    connections to them.</p>
+<p>Using the Wi-Fi NAN APIs, applications can advertise services, discover peers which are
+    advertising services, and connect to them.
+    Wi-Fi NAN is independent of Wi-Fi infrastructure (i.e. a device may or may
+    not be associated with an AP concurrent to using Wi-Fi NAN). </p>
+<p>The primary entry point to Wi-Fi NAN capabilities is the
+    {@link android.net.wifi.nan.WifiNanManager} class, which is acquired by calling
+    {@link android.content.Context#getSystemService(String)
+    Context.getSystemService(Context.WIFI_NAN_SERVICE)}</p>
+
+<p>Some APIs may require the following user permissions:</p>
+<ul>
+    <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
+    <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
+    <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi NAN
+    functionality.
+    If your application only works with Wi-Fi NAN (i.e. it should only be installed on devices which
+    support Wi-Fi NAN), declare so with a <a
+            href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+        {@code &lt;uses-feature&gt;}</a>
+    element in the manifest file:</p>
+<pre>
+&lt;manifest ...>
+    &lt;uses-feature android:name="android.hardware.wifi.nan" />
+    ...
+&lt;/manifest>
+</pre>
+<p>Alternatively, if you application does not require Wi-Fi NAN but can take advantage of it if
+    available, you can perform
+    the check at run-time in your code using {@link
+    android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
+    android.content.pm.PackageManager#FEATURE_WIFI_NAN}:</p>
+<pre>
+    getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)
+</pre>
+</BODY>
+</HTML>
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index a0cb035..a68bcd9 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -66,19 +66,28 @@
 
     /* Device Capability bitmap */
     private static final int DEVICE_CAPAB_SERVICE_DISCOVERY         = 1;
+    @SuppressWarnings("unused")
     private static final int DEVICE_CAPAB_CLIENT_DISCOVERABILITY    = 1<<1;
+    @SuppressWarnings("unused")
     private static final int DEVICE_CAPAB_CONCURRENT_OPER           = 1<<2;
+    @SuppressWarnings("unused")
     private static final int DEVICE_CAPAB_INFRA_MANAGED             = 1<<3;
+    @SuppressWarnings("unused")
     private static final int DEVICE_CAPAB_DEVICE_LIMIT              = 1<<4;
     private static final int DEVICE_CAPAB_INVITATION_PROCEDURE      = 1<<5;
 
     /* Group Capability bitmap */
     private static final int GROUP_CAPAB_GROUP_OWNER                = 1;
+    @SuppressWarnings("unused")
     private static final int GROUP_CAPAB_PERSISTENT_GROUP           = 1<<1;
     private static final int GROUP_CAPAB_GROUP_LIMIT                = 1<<2;
+    @SuppressWarnings("unused")
     private static final int GROUP_CAPAB_INTRA_BSS_DIST             = 1<<3;
+    @SuppressWarnings("unused")
     private static final int GROUP_CAPAB_CROSS_CONN                 = 1<<4;
+    @SuppressWarnings("unused")
     private static final int GROUP_CAPAB_PERSISTENT_RECONN          = 1<<5;
+    @SuppressWarnings("unused")
     private static final int GROUP_CAPAB_GROUP_FORMATION            = 1<<6;
 
     /**
@@ -305,6 +314,7 @@
         return other.deviceAddress.equals(deviceAddress);
     }
 
+    @Override
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append("Device: ").append(deviceName);
@@ -320,6 +330,7 @@
     }
 
     /** Implement the Parcelable interface */
+    @Override
     public int describeContents() {
         return 0;
     }
@@ -340,6 +351,7 @@
     }
 
     /** Implement the Parcelable interface */
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(deviceName);
         dest.writeString(deviceAddress);
@@ -360,6 +372,7 @@
     /** Implement the Parcelable interface */
     public static final Creator<WifiP2pDevice> CREATOR =
         new Creator<WifiP2pDevice>() {
+            @Override
             public WifiP2pDevice createFromParcel(Parcel in) {
                 WifiP2pDevice device = new WifiP2pDevice();
                 device.deviceName = in.readString();
@@ -376,6 +389,7 @@
                 return device;
             }
 
+            @Override
             public WifiP2pDevice[] newArray(int size) {
                 return new WifiP2pDevice[size];
             }