Merge "jni: EventLog replace open coding with android_log_event_list"
diff --git a/Android.mk b/Android.mk
index db5dd01..cdb3834 100644
--- a/Android.mk
+++ b/Android.mk
@@ -494,7 +494,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp)
 
 LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp core-junit bouncycastle ext
+LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp legacy-test bouncycastle ext
 LOCAL_STATIC_JAVA_LIBRARIES := framework-protos
 
 LOCAL_MODULE := framework
diff --git a/api/current.txt b/api/current.txt
index 277c6f5..d22006d3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36704,6 +36704,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -36856,6 +36857,7 @@
     method public int getAsuLevel();
     method public int getDbm();
     method public int getLevel();
+    method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -37222,6 +37224,7 @@
     method public int getCallState();
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
+    method public boolean getDataEnabled();
     method public int getDataNetworkType();
     method public int getDataState();
     method public java.lang.String getDeviceId();
@@ -37266,6 +37269,7 @@
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
+    method public void setDataEnabled(boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
diff --git a/api/system-current.txt b/api/system-current.txt
index 6645d53..01d4f47 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -35032,6 +35032,7 @@
     field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
+    field public static final java.lang.String ACTION_WIFI_SAVED_NETWORK_SETTINGS = "android.settings.WIFI_SAVED_NETWORK_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
     field public static final java.lang.String AUTHORITY = "settings";
@@ -39639,6 +39640,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -39791,6 +39793,7 @@
     method public int getAsuLevel();
     method public int getDbm();
     method public int getLevel();
+    method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index f2202eb..e21afe2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36781,6 +36781,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -36933,6 +36934,7 @@
     method public int getAsuLevel();
     method public int getDbm();
     method public int getLevel();
+    method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -37299,6 +37301,7 @@
     method public int getCallState();
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
+    method public boolean getDataEnabled();
     method public int getDataNetworkType();
     method public int getDataState();
     method public java.lang.String getDeviceId();
@@ -37343,6 +37346,7 @@
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
+    method public void setDataEnabled(boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b5f1c2a..eaad3a7 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -1,79 +1,67 @@
 LOCAL_PATH:= $(call my-dir)
 
+app_process_common_shared_libs := \
+    libandroid_runtime \
+    libbinder \
+    libcutils \
+    libdl \
+    liblog \
+    libnativeloader \
+    libutils \
+
 # This is a list of libraries that need to be included in order to avoid
 # bad apps. This prevents a library from having a mismatch when resolving
 # new/delete from an app shared library.
 # See b/21032018 for more details.
-app_process_common_shared_libs := \
+app_process_common_shared_libs += \
     libwilhelm \
 
+app_process_common_static_libs := \
+    libsigchain \
+
+app_process_src_files := \
+    app_main.cpp \
+
+app_process_cflags := \
+    -Wall -Werror -Wunused -Wunreachable-code
+
+app_process_ldflags_32 := \
+    -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+app_process_ldflags_64 := \
+    -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= \
-    app_main.cpp
+LOCAL_SRC_FILES:= $(app_process_src_files)
 
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_32 := $(app_process_ldflags_32)
+LOCAL_LDFLAGS_64 := $(app_process_ldflags_64)
 
-LOCAL_SHARED_LIBRARIES := \
-    libdl \
-    libcutils \
-    libutils \
-    liblog \
-    libbinder \
-    libnativeloader \
-    libandroid_runtime \
-    $(app_process_common_shared_libs) \
+LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs)
 
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs)
 
 LOCAL_MODULE:= app_process
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := app_process32
 LOCAL_MODULE_STEM_64 := app_process64
 
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += $(app_process_cflags)
+
+# In SANITIZE_LITE mode, we create the sanitized binary in a separate location (but reuse
+# the same module). Using the same module also works around an issue with make: binaries
+# that depend on sanitized libraries will be relinked, even if they set LOCAL_SANITIZE := never.
+#
+# Also pull in the asanwrapper helper.
+ifeq ($(SANITIZE_LITE),true)
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
+LOCAL_REQUIRED_MODULES := asanwrapper
+endif
 
 include $(BUILD_EXECUTABLE)
 
 # Create a symlink from app_process to app_process32 or 64
 # depending on the target configuration.
+ifneq ($(SANITIZE_LITE),true)
 include  $(BUILD_SYSTEM)/executable_prefer_symlink.mk
-
-# Build a variant of app_process binary linked with ASan runtime.
-# ARM-only at the moment.
-ifeq ($(TARGET_ARCH),arm)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    app_main.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libutils \
-    liblog \
-    libbinder \
-    libandroid_runtime \
-    $(app_process_common_shared_libs) \
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-
-LOCAL_LDFLAGS := -ldl
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
-
-LOCAL_MODULE := app_process__asan
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := app_process32
-LOCAL_MODULE_STEM_64 := app_process64
-
-LOCAL_SANITIZE := address
-LOCAL_CLANG := true
-LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_EXECUTABLE)
-
-endif # ifeq($(TARGET_ARCH),arm)
+endif
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 5d27662..94d03e5 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -235,6 +235,7 @@
         // >=0: registered and advertising started
         private int mAdvertiserId;
         private boolean mIsAdvertising = false;
+        private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR;
 
         public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
                 AdvertiseData advertiseData, AdvertiseData scanResponse,
@@ -262,12 +263,11 @@
                     mLeAdvertisers.put(mAdvertiseCallback, this);
                 } else if (mAdvertiserId < 0) {
 
-                    // Registration timeout, reset mClientIf to -1 so no subsequent operations can
+                    // Registration timeout, reset mClientIf to -2 so no subsequent operations can
                     // proceed.
-                    if (mAdvertiserId == 0) mAdvertiserId = -2;
+                    if (mAdvertiserId == -1) mAdvertiserId = -2;
                     // Post internal error if registration failed.
-                    postStartFailure(mAdvertiseCallback,
-                            AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
+                    postStartFailure(mAdvertiseCallback, registrationError);
                 } else {
                     // Unregister application if it's already registered but advertise failed.
                     try {
@@ -318,6 +318,8 @@
                     } catch (RemoteException e) {
                         Log.e(TAG, "failed to start advertising", e);
                     }
+                } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
+                    registrationError = status;
                 }
                 // Registration failed.
                 mAdvertiserId = -2;
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 01c160f..b93bf59 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -144,7 +144,7 @@
      *         scorer.
      */
     public String getActiveScorerPackage() {
-        NetworkScorerAppData app = NetworkScorerAppManager.getActiveScorer(mContext);
+        NetworkScorerAppData app = new NetworkScorerAppManager(mContext).getActiveScorer();
         if (app == null) {
             return null;
         }
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 29291ca..ebb31c90 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -41,14 +41,17 @@
  *
  * @hide
  */
-public final class NetworkScorerAppManager {
+public class NetworkScorerAppManager {
     private static final String TAG = "NetworkScorerAppManager";
 
     private static final Intent SCORE_INTENT =
             new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
 
-    /** This class cannot be instantiated. */
-    private NetworkScorerAppManager() {}
+    private final Context mContext;
+
+    public NetworkScorerAppManager(Context context) {
+      mContext = context;
+    }
 
     public static class NetworkScorerAppData {
         /** Package name of this scorer app. */
@@ -108,7 +111,7 @@
      *
      * @return the list of scorers, or the empty list if there are no valid scorers.
      */
-    public static Collection<NetworkScorerAppData> getAllValidScorers(Context context) {
+    public Collection<NetworkScorerAppData> getAllValidScorers() {
         // Network scorer apps can only run as the primary user so exit early if we're not the
         // primary user.
         if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
@@ -116,7 +119,7 @@
         }
 
         List<NetworkScorerAppData> scorers = new ArrayList<>();
-        PackageManager pm = context.getPackageManager();
+        PackageManager pm = mContext.getPackageManager();
         // Only apps installed under the primary user of the device can be scorers.
         // TODO: http://b/23422763
         List<ResolveInfo> receivers =
@@ -179,10 +182,10 @@
      *     selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
      *     it was disabled or uninstalled).
      */
-    public static NetworkScorerAppData getActiveScorer(Context context) {
-        String scorerPackage = Settings.Global.getString(context.getContentResolver(),
+    public NetworkScorerAppData getActiveScorer() {
+        String scorerPackage = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.NETWORK_SCORER_APP);
-        return getScorer(context, scorerPackage);
+        return getScorer(scorerPackage);
     }
 
     /**
@@ -190,13 +193,12 @@
      *
      * <p>The caller must have permission to write to {@link android.provider.Settings.Global}.
      *
-     * @param context the context of the calling application
      * @param packageName the packageName of the new scorer to use. If null, scoring will be
      *     disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
      * @return true if the scorer was changed, or false if the package is not a valid scorer.
      */
-    public static boolean setActiveScorer(Context context, String packageName) {
-        String oldPackageName = Settings.Global.getString(context.getContentResolver(),
+    public boolean setActiveScorer(String packageName) {
+        String oldPackageName = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.NETWORK_SCORER_APP);
         if (TextUtils.equals(oldPackageName, packageName)) {
             // No change.
@@ -206,13 +208,13 @@
         Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
 
         if (packageName == null) {
-            Settings.Global.putString(context.getContentResolver(),
+            Settings.Global.putString(mContext.getContentResolver(),
                     Settings.Global.NETWORK_SCORER_APP, null);
             return true;
         } else {
             // We only make the change if the new package is valid.
-            if (getScorer(context, packageName) != null) {
-                Settings.Global.putString(context.getContentResolver(),
+            if (getScorer(packageName) != null) {
+                Settings.Global.putString(mContext.getContentResolver(),
                         Settings.Global.NETWORK_SCORER_APP, packageName);
                 return true;
             } else {
@@ -223,8 +225,8 @@
     }
 
     /** Determine whether the application with the given UID is the enabled scorer. */
-    public static boolean isCallerActiveScorer(Context context, int callingUid) {
-        NetworkScorerAppData defaultApp = getActiveScorer(context);
+    public boolean isCallerActiveScorer(int callingUid) {
+        NetworkScorerAppData defaultApp = getActiveScorer();
         if (defaultApp == null) {
             return false;
         }
@@ -233,16 +235,16 @@
         }
         // To be extra safe, ensure the caller holds the SCORE_NETWORKS permission. It always
         // should, since it couldn't become the active scorer otherwise, but this can't hurt.
-        return context.checkCallingPermission(Manifest.permission.SCORE_NETWORKS) ==
+        return mContext.checkCallingPermission(Manifest.permission.SCORE_NETWORKS) ==
                 PackageManager.PERMISSION_GRANTED;
     }
 
     /** Returns the {@link NetworkScorerAppData} for the given app, or null if it's not a scorer. */
-    public static NetworkScorerAppData getScorer(Context context, String packageName) {
+    public NetworkScorerAppData getScorer(String packageName) {
         if (TextUtils.isEmpty(packageName)) {
             return null;
         }
-        Collection<NetworkScorerAppData> applications = getAllValidScorers(context);
+        Collection<NetworkScorerAppData> applications = getAllValidScorers();
         for (NetworkScorerAppData app : applications) {
             if (packageName.equals(app.mPackageName)) {
                 return app;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5e9380e..2f93afd 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -370,6 +370,22 @@
             "android.settings.WIFI_IP_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Wi-Fi saved networks.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_WIFI_SAVED_NETWORK_SETTINGS =
+            "android.settings.WIFI_SAVED_NETWORK_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of Bluetooth.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index b0d45e1d1..be10608df 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1190,6 +1190,26 @@
         }
 
         /**
+         * Remove a state from the state machine. Will not remove the state if it is currently
+         * active or if it has any children in the hierarchy.
+         * @param state the state to remove
+         */
+        private void removeState(State state) {
+            StateInfo stateInfo = mStateInfo.get(state);
+            if (stateInfo == null || stateInfo.active) {
+                return;
+            }
+            boolean isParent = mStateInfo.values().stream()
+                    .filter(si -> si.parentStateInfo == stateInfo)
+                    .findAny()
+                    .isPresent();
+            if (isParent) {
+                return;
+            }
+            mStateInfo.remove(state);
+        }
+
+        /**
          * Constructor
          *
          * @param looper for dispatching messages
@@ -1337,6 +1357,14 @@
     }
 
     /**
+     * Removes a state from the state machine, unless it is currently active or if it has children.
+     * @param state state to remove
+     */
+    public final void removeState(State state) {
+        mSmHandler.removeState(state);
+    }
+
+    /**
      * Set the initial state. This must be invoked before
      * and messages are sent to the state machine.
      *
diff --git a/core/res/res/values-mcc259-mnc05/config.xml b/core/res/res/values-mcc259-mnc05/config.xml
deleted file mode 100644
index 065668c..0000000
--- a/core/res/res/values-mcc259-mnc05/config.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- The list of ril radio technologies (see ServiceState.java) which only support
-         a single data connection at one time.  This may change by carrier via
-         overlays (some don't support multiple pdp on UMTS).  All unlisted radio
-         tech types support unlimited types (practically only 2-4 used). -->
-    <integer-array name="config_onlySingleDcAllowed">
-        <item>1</item>  <!-- GPRS -->
-        <item>2</item>  <!-- EDGE -->
-        <item>3</item>  <!-- UMTS -->
-        <item>9</item>  <!-- HSDPA -->
-        <item>10</item> <!-- HSUPA -->
-        <item>11</item> <!-- HSPA -->
-        <item>14</item> <!-- LTE -->
-        <item>15</item> <!-- HSPAP -->
-    </integer-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0309f74..4c28284 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1867,19 +1867,6 @@
          where if the preferred is used we don't try the others. -->
     <bool name="config_dontPreferApn">false</bool>
 
-    <!-- The list of ril radio technologies (see ServiceState.java) which only support
-         a single data connection at one time.  This may change by carrier via
-         overlays (some don't support multiple pdp on UMTS).  All unlisted radio
-         tech types support unlimited types (practically only 2-4 used). -->
-    <integer-array name="config_onlySingleDcAllowed">
-        <item>4</item>  <!-- IS95A -->
-        <item>5</item>  <!-- IS95B -->
-        <item>6</item>  <!-- 1xRTT -->
-        <item>7</item>  <!-- EVDO_0 -->
-        <item>8</item>  <!-- EVDO_A -->
-        <item>12</item> <!-- EVDO_B -->
-    </integer-array>
-
     <!-- Set to true if after a provisioning apn the radio should be restarted -->
     <bool name="config_restartRadioAfterProvisioning">false</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 91f0251..27ddb5a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1648,7 +1648,6 @@
   <java-symbol type="array" name="config_testLocationProviders" />
   <java-symbol type="array" name="config_defaultNotificationVibePattern" />
   <java-symbol type="array" name="config_notificationFallbackVibePattern" />
-  <java-symbol type="array" name="config_onlySingleDcAllowed" />
   <java-symbol type="bool" name="config_useAttentionLight" />
   <java-symbol type="bool" name="config_animateScreenLights" />
   <java-symbol type="bool" name="config_automatic_brightness_available" />
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index e7aca78..02c2517 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -42,6 +42,8 @@
     @Mock private Context mMockContext;
     @Mock private PackageManager mMockPm;
 
+    private NetworkScorerAppManager mNetworkScorerAppManager;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -54,6 +56,7 @@
 
         MockitoAnnotations.initMocks(this);
         Mockito.when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+        mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext);
     }
 
     public void testGetAllValidScorers() throws Exception {
@@ -81,7 +84,7 @@
         setScorers(scorers);
 
         Iterator<NetworkScorerAppData> result =
-                NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
+                mNetworkScorerAppManager.getAllValidScorers().iterator();
 
         assertTrue(result.hasNext());
         NetworkScorerAppData next = result.next();
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index e6d3158..0a32e43 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -349,6 +349,7 @@
         assertCanBeHandled(new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS));
         assertCanBeHandled(new Intent(Settings.ACTION_WIFI_IP_SETTINGS));
         assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SETTINGS));
+        assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SAVED_NETWORK_SETTINGS));
         assertCanBeHandled(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
     }
 
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
new file mode 100644
index 0000000..fe5d8ca
--- /dev/null
+++ b/legacy-test/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build the legacy-test library
+# =============================
+# This contains the junit.framework classes that were in Android API level 25.
+include $(CLEAR_VARS)
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := core-junit-static
+
+LOCAL_MODULE := legacy-test
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/media/mca/filterfw/jni/jni_gl_environment.cpp b/media/mca/filterfw/jni/jni_gl_environment.cpp
index 096120e..823e88a 100644
--- a/media/mca/filterfw/jni/jni_gl_environment.cpp
+++ b/media/mca/filterfw/jni/jni_gl_environment.cpp
@@ -63,7 +63,8 @@
 };
 
 jboolean Java_android_filterfw_core_GLEnvironment_nativeAllocate(JNIEnv* env, jobject thiz) {
-  return ToJBool(WrapObjectInJava(new GLEnv(), env, thiz, true));
+  std::unique_ptr<GLEnv> glEnv(new GLEnv());
+  return ToJBool(WrapOwnedObjectInJava(std::move(glEnv), env, thiz, true));
 }
 
 jboolean Java_android_filterfw_core_GLEnvironment_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_gl_frame.cpp b/media/mca/filterfw/jni/jni_gl_frame.cpp
index b55bc5d..27b4cd2 100644
--- a/media/mca/filterfw/jni/jni_gl_frame.cpp
+++ b/media/mca/filterfw/jni/jni_gl_frame.cpp
@@ -48,13 +48,11 @@
                                                            jint height) {
   GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
   if (!gl_env_ptr) return JNI_FALSE;
-  GLFrame* frame = new GLFrame(gl_env_ptr);
+  std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
   if (frame->Init(width, height)) {
-    return ToJBool(WrapObjectInJava(frame, env, thiz, true));
-  } else {
-    delete frame;
-    return JNI_FALSE;
+    return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
   }
+  return JNI_FALSE;
 }
 
 jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithTexture(JNIEnv* env,
@@ -65,13 +63,11 @@
                                                                       jint height) {
   GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
   if (!gl_env_ptr) return JNI_FALSE;
-  GLFrame* frame = new GLFrame(gl_env_ptr);
+  std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
   if (frame->InitWithTexture(tex_id, width, height)) {
-    return ToJBool(WrapObjectInJava(frame, env, thiz, true));
-  } else {
-    delete frame;
-    return JNI_FALSE;
+    return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
   }
+  return JNI_FALSE;
 }
 
 jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithFbo(JNIEnv* env,
@@ -82,13 +78,11 @@
                                                                   jint height) {
   GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
   if (!gl_env_ptr) return JNI_FALSE;
-  GLFrame* frame = new GLFrame(gl_env_ptr);
+  std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
   if (frame->InitWithFbo(fbo_id, width, height)) {
-    return ToJBool(WrapObjectInJava(frame, env, thiz, true));
-  } else {
-    delete frame;
-    return JNI_FALSE;
+    return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
   }
+  return JNI_FALSE;
 }
 
 jboolean Java_android_filterfw_core_GLFrame_nativeAllocateExternal(JNIEnv* env,
@@ -96,13 +90,11 @@
                                                                    jobject gl_env) {
   GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
   if (!gl_env_ptr) return JNI_FALSE;
-  GLFrame* frame = new GLFrame(gl_env_ptr);
+  std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
   if (frame->InitWithExternalTexture()) {
-    return ToJBool(WrapObjectInJava(frame, env, thiz, true));
-  } else {
-    delete frame;
-    return JNI_FALSE;
+    return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
   }
+  return JNI_FALSE;
 }
 
 jboolean Java_android_filterfw_core_GLFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_native_frame.cpp b/media/mca/filterfw/jni/jni_native_frame.cpp
index c8f2352..1a11a19 100644
--- a/media/mca/filterfw/jni/jni_native_frame.cpp
+++ b/media/mca/filterfw/jni/jni_native_frame.cpp
@@ -35,7 +35,8 @@
 jboolean Java_android_filterfw_core_NativeFrame_nativeAllocate(JNIEnv* env,
                                                                jobject thiz,
                                                                jint size) {
-  return ToJBool(WrapObjectInJava(new NativeFrame(size), env, thiz, true));
+  std::unique_ptr<NativeFrame> frame(new NativeFrame(size));
+  return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
 }
 
 jboolean Java_android_filterfw_core_NativeFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_native_program.cpp b/media/mca/filterfw/jni/jni_native_program.cpp
index b30b769..1424607 100644
--- a/media/mca/filterfw/jni/jni_native_program.cpp
+++ b/media/mca/filterfw/jni/jni_native_program.cpp
@@ -28,7 +28,8 @@
 using android::filterfw::NativeProgram;
 
 jboolean Java_android_filterfw_core_NativeProgram_allocate(JNIEnv* env, jobject thiz) {
-  return ToJBool(WrapObjectInJava(new NativeProgram(), env, thiz, true));
+  std::unique_ptr<NativeProgram> program(new NativeProgram());
+  return ToJBool(WrapOwnedObjectInJava(std::move(program), env, thiz, true));
 }
 
 jboolean Java_android_filterfw_core_NativeProgram_deallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_shader_program.cpp b/media/mca/filterfw/jni/jni_shader_program.cpp
index 19f43cd..98be04c 100644
--- a/media/mca/filterfw/jni/jni_shader_program.cpp
+++ b/media/mca/filterfw/jni/jni_shader_program.cpp
@@ -46,21 +46,14 @@
   // Create the shader
   if (!fragment_shader || !gl_env_ptr)
     return false;
-  else if (!vertex_shader)
-    return ToJBool(WrapObjectInJava(new ShaderProgram(
-      gl_env_ptr,
-      ToCppString(env, fragment_shader)),
-      env,
-      thiz,
-      true));
+
+  std::unique_ptr<ShaderProgram> shader;
+  if (!vertex_shader)
+    shader.reset(new ShaderProgram(gl_env_ptr, ToCppString(env, fragment_shader)));
   else
-    return ToJBool(WrapObjectInJava(new ShaderProgram(
-      gl_env_ptr,
-      ToCppString(env, vertex_shader),
-      ToCppString(env, fragment_shader)),
-      env,
-      thiz,
-      true));
+    shader.reset(new ShaderProgram(gl_env_ptr, ToCppString(env, vertex_shader),
+                                   ToCppString(env, fragment_shader)));
+  return ToJBool(WrapOwnedObjectInJava(std::move(shader), env, thiz, true));
 }
 
 jboolean Java_android_filterfw_core_ShaderProgram_deallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h
index 11c0871..803ed29 100644
--- a/media/mca/filterfw/jni/jni_util.h
+++ b/media/mca/filterfw/jni/jni_util.h
@@ -16,6 +16,7 @@
 
 #include <jni.h>
 
+#include <memory>
 #include <unordered_map>
 #include <string>
 
@@ -214,6 +215,17 @@
   return pool ? pool->WrapObject(c_object, env, j_object, owns) : false;
 }
 
+// Calls WrapObjectInJava, safely freeing c_object if object creation fails.
+template<typename T>
+bool WrapOwnedObjectInJava(std::unique_ptr<T> c_object, JNIEnv* env,
+                           jobject j_object, bool owns) {
+  if (!WrapObjectInJava<T>(c_object.get(), env, j_object, owns))
+    return false;
+  // If we succeeded, a Java object now owns our c object; don't free it.
+  c_object.release();
+  return true;
+}
+
 // Creates a new Java instance, which wraps the passed C++ instance. Returns
 // the wrapped object or JNI_NULL if there was an error. Pass true to owns, if
 // the Java layer should own the object.
diff --git a/media/mca/filterfw/jni/jni_vertex_frame.cpp b/media/mca/filterfw/jni/jni_vertex_frame.cpp
index caae938..d0439fe 100644
--- a/media/mca/filterfw/jni/jni_vertex_frame.cpp
+++ b/media/mca/filterfw/jni/jni_vertex_frame.cpp
@@ -24,7 +24,8 @@
 jboolean Java_android_filterfw_core_VertexFrame_nativeAllocate(JNIEnv* env,
                                                                jobject thiz,
                                                                jint size) {
-  return ToJBool(WrapObjectInJava(new VertexFrame(size), env, thiz, true));
+  std::unique_ptr<VertexFrame> frame(new VertexFrame(size));
+  return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
 }
 
 jboolean Java_android_filterfw_core_VertexFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 83d6344..756cb30 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 
 import java.io.FileDescriptor;
@@ -61,6 +62,7 @@
     private static final boolean DBG = false;
 
     private final Context mContext;
+    private final NetworkScorerAppManager mNetworkScorerAppManager;
     private final Map<Integer, INetworkScoreCache> mScoreCaches;
     /** Lock used to update mPackageMonitor when scorer package changes occur. */
     private final Object mPackageMonitorLock = new Object[0];
@@ -131,7 +133,7 @@
                             + ", forceUnbind=" + forceUnbind);
                 }
                 final NetworkScorerAppData activeScorer =
-                        NetworkScorerAppManager.getActiveScorer(mContext);
+                        mNetworkScorerAppManager.getActiveScorer();
                 if (activeScorer == null) {
                     // Package change has invalidated a scorer, this will also unbind any service
                     // connection.
@@ -152,7 +154,13 @@
     }
 
     public NetworkScoreService(Context context) {
+      this(context, new NetworkScorerAppManager(context));
+    }
+
+    @VisibleForTesting
+    NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
         mContext = context;
+        mNetworkScorerAppManager = networkScoreAppManager;
         mScoreCaches = new HashMap<>();
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
         // TODO: Need to update when we support per-user scorers. http://b/23422763
@@ -171,7 +179,7 @@
             String defaultPackage = mContext.getResources().getString(
                     R.string.config_defaultNetworkScorerPackageName);
             if (!TextUtils.isEmpty(defaultPackage)) {
-                NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
+                mNetworkScorerAppManager.setActiveScorer(defaultPackage);
             }
             Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
         }
@@ -192,7 +200,7 @@
 
     private void registerPackageMonitorIfNeeded() {
         if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
-        NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
+        NetworkScorerAppData scorer = mNetworkScorerAppManager.getActiveScorer();
         synchronized (mPackageMonitorLock) {
             // Unregister the current monitor if needed.
             if (mPackageMonitor != null) {
@@ -220,7 +228,7 @@
 
     private void bindToScoringServiceIfNeeded() {
         if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
-        NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
+        NetworkScorerAppData scorerData = mNetworkScorerAppManager.getActiveScorer();
         bindToScoringServiceIfNeeded(scorerData);
     }
 
@@ -257,7 +265,7 @@
 
     @Override
     public boolean updateScores(ScoredNetwork[] networks) {
-        if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
+        if (!mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid())) {
             throw new SecurityException("Caller with UID " + getCallingUid() +
                     " is not the active scorer.");
         }
@@ -296,7 +304,7 @@
     public boolean clearScores() {
         // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
         // should be allowed to flush all scores.
-        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
                 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
                         PackageManager.PERMISSION_GRANTED) {
             clearInternal();
@@ -326,7 +334,7 @@
     public void disableScoring() {
         // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
         // should be allowed to disable scoring.
-        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
                 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
                         PackageManager.PERMISSION_GRANTED) {
             // The return value is discarded here because at this point, the call should always
@@ -350,8 +358,8 @@
             // only be allowing valid apps to be set as scorers, so failure here should be rare.
             clearInternal();
             // Get the scorer that is about to be replaced, if any, so we can notify it directly.
-            NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
-            boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+            NetworkScorerAppData prevScorer = mNetworkScorerAppManager.getActiveScorer();
+            boolean result = mNetworkScorerAppManager.setActiveScorer(packageName);
             // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
             // then we'll attempt to restore the previous binding (if any), otherwise an attempt
             // will be made to bind to the new scorer.
@@ -409,7 +417,7 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
-        NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
+        NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
         if (currentScorer == null) {
             writer.println("Scoring is disabled.");
             return;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2932a1a..62f4f19 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1381,7 +1381,6 @@
         }
 
         Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         Bundle data = new Bundle();
         signalStrength.fillInNotifierBundle(data);
         intent.putExtras(data);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f54d23..d048af2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2891,9 +2891,11 @@
 
     @Override
     public void batterySendBroadcast(Intent intent) {
-        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                AppOpsManager.OP_NONE, null, false, false,
-                -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
+        synchronized (this) {
+            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                    AppOpsManager.OP_NONE, null, false, false,
+                    -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index ba799f2c..96a804b 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -304,15 +304,19 @@
      * @param crashInfo describing the failure
      */
     void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+
         final long origId = Binder.clearCallingIdentity();
         try {
-            crashApplicationInner(r, crashInfo);
+            crashApplicationInner(r, crashInfo, callingPid, callingUid);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-    void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+    void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
+            int callingPid, int callingUid) {
         long timeMillis = System.currentTimeMillis();
         String shortMsg = crashInfo.exceptionClassName;
         String longMsg = crashInfo.exceptionMessage;
@@ -331,7 +335,7 @@
              * finish now and don't show the app error dialog.
              */
             if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
-                    timeMillis)) {
+                    timeMillis, callingPid, callingUid)) {
                 return;
             }
 
@@ -433,15 +437,16 @@
     private boolean handleAppCrashInActivityController(ProcessRecord r,
                                                        ApplicationErrorReport.CrashInfo crashInfo,
                                                        String shortMsg, String longMsg,
-                                                       String stackTrace, long timeMillis) {
+                                                       String stackTrace, long timeMillis,
+                                                       int callingPid, int callingUid) {
         if (mService.mController == null) {
             return false;
         }
 
         try {
             String name = r != null ? r.processName : null;
-            int pid = r != null ? r.pid : Binder.getCallingPid();
-            int uid = r != null ? r.info.uid : Binder.getCallingUid();
+            int pid = r != null ? r.pid : callingPid;
+            int uid = r != null ? r.info.uid : callingUid;
             if (!mService.mController.appCrashed(name, pid,
                     shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
                 if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 1ef4a9f..acdcc72 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -219,19 +219,20 @@
 
                 final String dexoptType;
                 String oatDir = null;
-                switch (dexoptNeeded) {
+                boolean isOdexLocation = (dexoptNeeded < 0);
+                switch (Math.abs(dexoptNeeded)) {
                     case DexFile.NO_DEXOPT_NEEDED:
                         continue;
-                    case DexFile.DEX2OAT_NEEDED:
+                    case DexFile.DEX2OAT_FROM_SCRATCH:
+                    case DexFile.DEX2OAT_FOR_BOOT_IMAGE:
+                    case DexFile.DEX2OAT_FOR_FILTER:
+                    case DexFile.DEX2OAT_FOR_RELOCATION:
                         dexoptType = "dex2oat";
                         oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                         break;
-                    case DexFile.PATCHOAT_NEEDED:
+                    case DexFile.PATCHOAT_FOR_RELOCATION:
                         dexoptType = "patchoat";
                         break;
-                    case DexFile.SELF_PATCHOAT_NEEDED:
-                        dexoptType = "self patchoat";
-                        break;
                     default:
                         throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
                 }
@@ -383,7 +384,7 @@
         protected int adjustDexoptNeeded(int dexoptNeeded) {
             // Ensure compilation, no matter the current state.
             // TODO: The return value is wrong when patchoat is needed.
-            return DexFile.DEX2OAT_NEEDED;
+            return DexFile.DEX2OAT_FROM_SCRATCH;
         }
     }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d064cd9..a1316a9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -208,6 +208,14 @@
             KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
 
     /**
+     * List of RIL radio technologies (See {@link ServiceState} {@code RIL_RADIO_TECHNOLOGY_*}
+     * constants) which support only a single data connection at a time. Some carriers do not
+     * support multiple pdp on UMTS.
+     */
+    public static final String
+            KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
+
+    /**
      * Override the platform's notion of a network operator being considered roaming.
      * Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
      */
@@ -848,6 +856,15 @@
         sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{"default", "mms", "dun", "supl"});
 
+        sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
+                new int[]{
+                    4, /* IS95A */
+                    5, /* IS95B */
+                    6, /* 1xRTT */
+                    7, /* EVDO_0 */
+                    8, /* EVDO_A */
+                    12 /* EVDO_B */
+                });
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index addf7ef..4137853 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -141,6 +141,16 @@
     }
 
     /**
+     * Get the GSM timing advance between 0..219 symbols (normally 0..63).
+     * Integer.MAX_VALUE is reported when there is no RR connection.
+     * Refer to 3GPP 45.010 Sec 5.8
+     * @return the current GSM timing advance, if available.
+     */
+    public int getTimingAdvance() {
+        return mTimingAdvance;
+    }
+
+    /**
      * Get the signal strength as dBm
      */
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 434caad..0d07a40 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -220,8 +220,10 @@
     }
 
     /**
-     * Get the timing advance value for LTE.
-     * See 3GPP xxxx
+     * Get the timing advance value for LTE, as a value between 0..63.
+     * Integer.MAX_VALUE is reported when there is no active RRC
+     * connection. Refer to 3GPP 36.213 Sec 4.2.3
+     * @return the LTE timing advance, if available.
      */
     public int getTimingAdvance() {
         return mTimingAdvance;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ea87fa3..fe33300 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 /**
  * Contains phone state and service related information.
@@ -579,6 +580,24 @@
     }
 
     /**
+     * Get current registered operator name in long alphanumeric format if
+     * available or short otherwise.
+     *
+     * @see #getOperatorAlphaLong
+     * @see #getOperatorAlphaShort
+     *
+     * @return name of operator, null if unregistered or unknown
+     * @hide
+     */
+    public String getOperatorAlpha() {
+        if (TextUtils.isEmpty(mVoiceOperatorAlphaLong)) {
+            return mVoiceOperatorAlphaShort;
+        }
+
+        return mVoiceOperatorAlphaLong;
+    }
+
+    /**
      * Get current registered operator numeric id.
      *
      * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4e4e75d..ca17c06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4557,10 +4557,19 @@
         return false;
     }
 
-    /** @hide */
-    @SystemApi
+    /**
+     * Turns mobile data on or off.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     *     calling app has carrier privileges.
+     *
+     * @param enable Whether to enable mobile data.
+     *
+     * @see #hasCarrierPrivileges
+     */
     public void setDataEnabled(boolean enable) {
-        setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable);
+        setDataEnabled(getSubId(), enable);
     }
 
     /** @hide */
@@ -4576,10 +4585,20 @@
         }
     }
 
-    /** @hide */
-    @SystemApi
+    /**
+     * Returns whether mobile data is enabled or not.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+     *     calling app has carrier privileges.
+     *
+     * @return true if mobile data is enabled.
+     *
+     * @see #hasCarrierPrivileges
+     */
     public boolean getDataEnabled() {
-        return getDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId());
+        return getDataEnabled(getSubId());
     }
 
     /** @hide */
diff --git a/test-runner/src/junit/MODULE_LICENSE_CPL b/test-runner/src/junit/MODULE_LICENSE_CPL
deleted file mode 100644
index 541dbb5..0000000
--- a/test-runner/src/junit/MODULE_LICENSE_CPL
+++ /dev/null
@@ -1 +0,0 @@
-http://www.opensource.org/licenses/cpl1.0.php
diff --git a/test-runner/src/junit/runner/ClassPathTestCollector.java b/test-runner/src/junit/runner/ClassPathTestCollector.java
deleted file mode 100644
index f48ddee..0000000
--- a/test-runner/src/junit/runner/ClassPathTestCollector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-import java.io.*;
-
-/**
- * An implementation of a TestCollector that consults the
- * class path. It considers all classes on the class path
- * excluding classes in JARs. It leaves it up to subclasses
- * to decide whether a class is a runnable Test.
- *
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public abstract class ClassPathTestCollector implements TestCollector {
-
-    static final int SUFFIX_LENGTH= ".class".length();
-
-    public ClassPathTestCollector() {
-    }
-
-    public Enumeration collectTests() {
-        String classPath= System.getProperty("java.class.path");
-        Hashtable result = collectFilesInPath(classPath);
-        return result.elements();
-    }
-
-    public Hashtable collectFilesInPath(String classPath) {
-        Hashtable result= collectFilesInRoots(splitClassPath(classPath));
-        return result;
-    }
-
-    Hashtable collectFilesInRoots(Vector roots) {
-        Hashtable result= new Hashtable(100);
-        Enumeration e= roots.elements();
-        while (e.hasMoreElements())
-            gatherFiles(new File((String)e.nextElement()), "", result);
-        return result;
-    }
-
-    void gatherFiles(File classRoot, String classFileName, Hashtable result) {
-        File thisRoot= new File(classRoot, classFileName);
-        if (thisRoot.isFile()) {
-            if (isTestClass(classFileName)) {
-                String className= classNameFromFile(classFileName);
-                result.put(className, className);
-            }
-            return;
-        }
-        String[] contents= thisRoot.list();
-        if (contents != null) {
-            for (int i= 0; i < contents.length; i++)
-                gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);
-        }
-    }
-
-    Vector splitClassPath(String classPath) {
-        Vector result= new Vector();
-        String separator= System.getProperty("path.separator");
-        StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
-        while (tokenizer.hasMoreTokens())
-            result.addElement(tokenizer.nextToken());
-        return result;
-    }
-
-    protected boolean isTestClass(String classFileName) {
-        return
-                classFileName.endsWith(".class") &&
-                classFileName.indexOf('$') < 0 &&
-                classFileName.indexOf("Test") > 0;
-    }
-
-    protected String classNameFromFile(String classFileName) {
-        // convert /a/b.class to a.b
-        String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
-        String s2= s.replace(File.separatorChar, '.');
-        if (s2.startsWith("."))
-            return s2.substring(1);
-        return s2;
-    }
-}
diff --git a/test-runner/src/junit/runner/FailureDetailView.java b/test-runner/src/junit/runner/FailureDetailView.java
deleted file mode 100644
index c846191..0000000
--- a/test-runner/src/junit/runner/FailureDetailView.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package junit.runner;
-
-// The following line was removed for compatibility with Android libraries.
-//import java.awt.Component;
-
-import junit.framework.*;
-
-/**
- * A view to show a details about a failure
- * {@hide} - Not needed for 1.0 SDK
- */
-public interface FailureDetailView {
-    // The following definition was removed for compatibility with Android
-    // libraries.
-    //  /**
-    //   * Returns the component used to present the TraceView
-    //   */
-    //  public Component getComponent();
-
-    /**
-     * Shows details of a TestFailure
-     */
-    public void showFailure(TestFailure failure);
-    /**
-     * Clears the view
-     */
-    public void clear();
-}
diff --git a/test-runner/src/junit/runner/LoadingTestCollector.java b/test-runner/src/junit/runner/LoadingTestCollector.java
deleted file mode 100644
index 9101900..0000000
--- a/test-runner/src/junit/runner/LoadingTestCollector.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package junit.runner;
-
-import java.lang.reflect.*;
-import junit.framework.*;
-
-/**
- * An implementation of a TestCollector that loads
- * all classes on the class path and tests whether
- * it is assignable from Test or provides a static suite method.
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public class LoadingTestCollector extends ClassPathTestCollector {
-
-    TestCaseClassLoader fLoader;
-
-    public LoadingTestCollector() {
-        fLoader= new TestCaseClassLoader();
-    }
-
-    protected boolean isTestClass(String classFileName) {
-        try {
-            if (classFileName.endsWith(".class")) {
-                Class testClass= classFromFile(classFileName);
-                return (testClass != null) && isTestClass(testClass);
-            }
-        }
-        catch (ClassNotFoundException expected) {
-        }
-        catch (NoClassDefFoundError notFatal) {
-        }
-        return false;
-    }
-
-    Class classFromFile(String classFileName) throws ClassNotFoundException {
-        String className= classNameFromFile(classFileName);
-        if (!fLoader.isExcluded(className))
-            return fLoader.loadClass(className, false);
-        return null;
-    }
-
-    boolean isTestClass(Class testClass) {
-        if (hasSuiteMethod(testClass))
-            return true;
-        if (Test.class.isAssignableFrom(testClass) &&
-                Modifier.isPublic(testClass.getModifiers()) &&
-                hasPublicConstructor(testClass))
-            return true;
-        return false;
-    }
-
-    boolean hasSuiteMethod(Class testClass) {
-        try {
-            testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
-        } catch(Exception e) {
-            return false;
-        }
-        return true;
-    }
-
-    boolean hasPublicConstructor(Class testClass) {
-        try {
-            TestSuite.getTestConstructor(testClass);
-        } catch(NoSuchMethodException e) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java b/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java
deleted file mode 100644
index c4d80d0..0000000
--- a/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package junit.runner;
-
-/**
- * A TestSuite loader that can reload classes.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class ReloadingTestSuiteLoader implements TestSuiteLoader {
-
-    public Class load(String suiteClassName) throws ClassNotFoundException {
-        return createLoader().loadClass(suiteClassName, true);
-    }
-
-    public Class reload(Class aClass) throws ClassNotFoundException {
-        return createLoader().loadClass(aClass.getName(), true);
-    }
-
-    protected TestCaseClassLoader createLoader() {
-        return new TestCaseClassLoader();
-    }
-}
diff --git a/test-runner/src/junit/runner/SimpleTestCollector.java b/test-runner/src/junit/runner/SimpleTestCollector.java
deleted file mode 100644
index 6cb0e19..0000000
--- a/test-runner/src/junit/runner/SimpleTestCollector.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package junit.runner;
-
-/**
- * An implementation of a TestCollector that considers
- * a class to be a test class when it contains the
- * pattern "Test" in its name
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public class SimpleTestCollector extends ClassPathTestCollector {
-
-    public SimpleTestCollector() {
-    }
-
-    protected boolean isTestClass(String classFileName) {
-        return
-                classFileName.endsWith(".class") &&
-                classFileName.indexOf('$') < 0 &&
-                classFileName.indexOf("Test") > 0;
-    }
-}
diff --git a/test-runner/src/junit/runner/Sorter.java b/test-runner/src/junit/runner/Sorter.java
deleted file mode 100644
index 8d9341d..0000000
--- a/test-runner/src/junit/runner/Sorter.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-
-/**
- * A custom quick sort with support to customize the swap behaviour.
- * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
- * classes because of the JDK 1.1.7 compatibility.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class Sorter {
-    public static interface Swapper {
-        public void swap(Vector values, int left, int right);
-    }
-
-    public static void sortStrings(Vector values , int left, int right, Swapper swapper) {
-        int oleft= left;
-        int oright= right;
-        String mid= (String)values.elementAt((left + right) / 2);
-        do {
-            while (((String)(values.elementAt(left))).compareTo(mid) < 0)
-                left++;
-            while (mid.compareTo((String)(values.elementAt(right))) < 0)
-                right--;
-            if (left <= right) {
-                swapper.swap(values, left, right);
-                left++;
-                right--;
-            }
-        } while (left <= right);
-
-        if (oleft < right)
-            sortStrings(values, oleft, right, swapper);
-        if (left < oright)
-            sortStrings(values, left, oright, swapper);
-    }
-}
diff --git a/test-runner/src/junit/runner/TestCaseClassLoader.java b/test-runner/src/junit/runner/TestCaseClassLoader.java
deleted file mode 100644
index 09eec7f..0000000
--- a/test-runner/src/junit/runner/TestCaseClassLoader.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-import java.io.*;
-import java.net.URL;
-import java.util.zip.*;
-
-/**
- * A custom class loader which enables the reloading
- * of classes for each test run. The class loader
- * can be configured with a list of package paths that
- * should be excluded from loading. The loading
- * of these packages is delegated to the system class
- * loader. They will be shared across test runs.
- * <p>
- * The list of excluded package paths is specified in
- * a properties file "excluded.properties" that is located in
- * the same place as the TestCaseClassLoader class.
- * <p>
- * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
- * from jar files.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class TestCaseClassLoader extends ClassLoader {
-    /** scanned class path */
-    private Vector fPathItems;
-    /** default excluded paths */
-    private String[] defaultExclusions= {
-            "junit.framework.",
-            "junit.extensions.",
-            "junit.runner."
-    };
-    /** name of excluded properties file */
-    static final String EXCLUDED_FILE= "excluded.properties";
-    /** excluded paths */
-    private Vector fExcluded;
-
-    /**
-     * Constructs a TestCaseLoader. It scans the class path
-     * and the excluded package paths
-     */
-    public TestCaseClassLoader() {
-        this(System.getProperty("java.class.path"));
-    }
-
-    /**
-     * Constructs a TestCaseLoader. It scans the class path
-     * and the excluded package paths
-     */
-    public TestCaseClassLoader(String classPath) {
-        scanPath(classPath);
-        readExcludedPackages();
-    }
-
-    private void scanPath(String classPath) {
-        String separator= System.getProperty("path.separator");
-        fPathItems= new Vector(10);
-        StringTokenizer st= new StringTokenizer(classPath, separator);
-        while (st.hasMoreTokens()) {
-            fPathItems.addElement(st.nextToken());
-        }
-    }
-
-    public URL getResource(String name) {
-        return ClassLoader.getSystemResource(name);
-    }
-
-    public InputStream getResourceAsStream(String name) {
-        return ClassLoader.getSystemResourceAsStream(name);
-    }
-
-    public boolean isExcluded(String name) {
-        for (int i= 0; i < fExcluded.size(); i++) {
-            if (name.startsWith((String) fExcluded.elementAt(i))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public synchronized Class loadClass(String name, boolean resolve)
-            throws ClassNotFoundException {
-
-        Class c= findLoadedClass(name);
-        if (c != null)
-            return c;
-        //
-        // Delegate the loading of excluded classes to the
-        // standard class loader.
-        //
-        if (isExcluded(name)) {
-            try {
-                c= findSystemClass(name);
-                return c;
-            } catch (ClassNotFoundException e) {
-                // keep searching
-            }
-        }
-        if (c == null) {
-            byte[] data= lookupClassData(name);
-            if (data == null)
-                throw new ClassNotFoundException();
-            c= defineClass(name, data, 0, data.length);
-        }
-        if (resolve)
-            resolveClass(c);
-        return c;
-    }
-
-    private byte[] lookupClassData(String className) throws ClassNotFoundException {
-        byte[] data= null;
-        for (int i= 0; i < fPathItems.size(); i++) {
-            String path= (String) fPathItems.elementAt(i);
-            String fileName= className.replace('.', '/')+".class";
-            if (isJar(path)) {
-                data= loadJarData(path, fileName);
-            } else {
-                data= loadFileData(path, fileName);
-            }
-            if (data != null)
-                return data;
-        }
-        throw new ClassNotFoundException(className);
-    }
-
-    boolean isJar(String pathEntry) {
-        return pathEntry.endsWith(".jar") ||
-                pathEntry.endsWith(".apk") ||
-                pathEntry.endsWith(".zip");
-    }
-
-    private byte[] loadFileData(String path, String fileName) {
-        File file= new File(path, fileName);
-        if (file.exists()) {
-            return getClassData(file);
-        }
-        return null;
-    }
-
-    private byte[] getClassData(File f) {
-        try {
-            FileInputStream stream= new FileInputStream(f);
-            ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
-            byte[] b= new byte[1000];
-            int n;
-            while ((n= stream.read(b)) != -1)
-                out.write(b, 0, n);
-            stream.close();
-            out.close();
-            return out.toByteArray();
-
-        } catch (IOException e) {
-        }
-        return null;
-    }
-
-    private byte[] loadJarData(String path, String fileName) {
-        ZipFile zipFile= null;
-        InputStream stream= null;
-        File archive= new File(path);
-        if (!archive.exists())
-            return null;
-        try {
-            zipFile= new ZipFile(archive);
-        } catch(IOException io) {
-            return null;
-        }
-        ZipEntry entry= zipFile.getEntry(fileName);
-        if (entry == null)
-            return null;
-        int size= (int) entry.getSize();
-        try {
-            stream= zipFile.getInputStream(entry);
-            byte[] data= new byte[size];
-            int pos= 0;
-            while (pos < size) {
-                int n= stream.read(data, pos, data.length - pos);
-                pos += n;
-            }
-            zipFile.close();
-            return data;
-        } catch (IOException e) {
-        } finally {
-            try {
-                if (stream != null)
-                    stream.close();
-            } catch (IOException e) {
-            }
-        }
-        return null;
-    }
-
-    private void readExcludedPackages() {
-        fExcluded= new Vector(10);
-        for (int i= 0; i < defaultExclusions.length; i++)
-            fExcluded.addElement(defaultExclusions[i]);
-
-        InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
-        if (is == null)
-            return;
-        Properties p= new Properties();
-        try {
-            p.load(is);
-        }
-        catch (IOException e) {
-            return;
-        } finally {
-            try {
-                is.close();
-            } catch (IOException e) {
-            }
-        }
-        for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
-            String key= (String)e.nextElement();
-            if (key.startsWith("excluded.")) {
-                String path= p.getProperty(key);
-                path= path.trim();
-                if (path.endsWith("*"))
-                    path= path.substring(0, path.length()-1);
-                if (path.length() > 0)
-                    fExcluded.addElement(path);
-            }
-        }
-    }
-}
diff --git a/test-runner/src/junit/runner/TestCollector.java b/test-runner/src/junit/runner/TestCollector.java
deleted file mode 100644
index 3ac9d9e..0000000
--- a/test-runner/src/junit/runner/TestCollector.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-
-
-/**
- * Collects Test class names to be presented
- * by the TestSelector.
- * @see TestSelector
- * {@hide} - Not needed for 1.0 SDK
- */
-public interface TestCollector {
-    /**
-     * Returns an enumeration of Strings with qualified class names
-     */
-    public Enumeration collectTests();
-}
diff --git a/test-runner/src/junit/runner/excluded.properties b/test-runner/src/junit/runner/excluded.properties
deleted file mode 100644
index 3284628..0000000
--- a/test-runner/src/junit/runner/excluded.properties
+++ /dev/null
@@ -1,12 +0,0 @@
-#
-# The list of excluded package paths for the TestCaseClassLoader
-#
-excluded.0=sun.*
-excluded.1=com.sun.*
-excluded.2=org.omg.*
-excluded.3=javax.*
-excluded.4=sunw.*
-excluded.5=java.*
-excluded.6=org.w3c.dom.*
-excluded.7=org.xml.sax.*
-excluded.8=net.jini.*
diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html
deleted file mode 100644
index f08fa70..0000000
--- a/test-runner/src/junit/runner/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-</BODY>
-</HTML>
diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html
deleted file mode 100644
index 723f2ae..0000000
--- a/test-runner/src/junit/textui/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-{@hide} - Not needed for 1.0 SDK
-</BODY>
-</HTML>
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index 8a81d0b..4656476 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -28,7 +28,7 @@
 			layoutlib_api-prebuilt \
 			tools-common-prebuilt \
 			sdk-common \
-			junit
+			junit-host
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk
index dafb9c6..61e381d 100644
--- a/tools/layoutlib/create/tests/Android.mk
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_MODULE := layoutlib-create-tests
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := layoutlib_create junit
+LOCAL_JAVA_LIBRARIES := layoutlib_create junit-host
 LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
index 09d95ff..769db6b 100644
--- a/tools/preload2/Android.mk
+++ b/tools/preload2/Android.mk
@@ -12,7 +12,7 @@
 
 # For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
 # convenience (and to not depend on internal JDK APIs).
-LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit
+LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit-host
 
 LOCAL_MODULE:= preload2
 
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
index 36dbc1c..322b62f 100644
--- a/tools/preload2/preload-tool
+++ b/tools/preload2/preload-tool
@@ -34,4 +34,4 @@
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
 ANDROID_ROOT=$PROG_DIR/..
 
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main
+java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
index ca5b0e0..c42a19b 100644
--- a/tools/preload2/src/com/android/preload/Main.java
+++ b/tools/preload2/src/com/android/preload/Main.java
@@ -32,12 +32,18 @@
 import com.android.preload.classdataretrieval.ClassDataRetriever;
 import com.android.preload.classdataretrieval.hprof.Hprof;
 import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.UI;
+import com.android.preload.ui.IUI;
+import com.android.preload.ui.SequenceUI;
+import com.android.preload.ui.SwingUI;
 
+import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 
 import javax.swing.Action;
 import javax.swing.DefaultListModel;
@@ -66,7 +72,7 @@
     private DumpTableModel dataTableModel;
     private DefaultListModel<Client> clientListModel;
 
-    private UI ui;
+    private IUI ui;
 
     // Actions that need to be updated once a device is selected.
     private Collection<DeviceSpecific> deviceSpecificActions;
@@ -85,17 +91,30 @@
             + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
             + "(.*\\$NoPreloadHolder$)";
 
+    public final static String SCAN_ALL_CMD = "scan-all";
+    public final static String SCAN_PACKAGE_CMD = "scan";
+    public final static String COMPUTE_FILE_CMD = "comp";
+    public final static String EXPORT_CMD = "export";
+    public final static String IMPORT_CMD = "import";
+
     /**
      * @param args
      */
     public static void main(String[] args) {
-        Main m = new Main();
-        top = m;
+        Main m;
+        if (args.length > 0 && args[0].equals("--seq")) {
+            m = createSequencedMain(args);
+        } else {
+            m = new Main(new SwingUI());
+        }
 
+        top = m;
         m.startUp();
     }
 
-    public Main() {
+    public Main(IUI ui) {
+        this.ui = ui;
+
         clientListModel = new DefaultListModel<Client>();
         dataTableModel = new DumpTableModel();
 
@@ -124,11 +143,74 @@
             }
         }
 
-        ui = new UI(clientListModel, dataTableModel, actions);
-        ui.setVisible(true);
+        ui.prepare(clientListModel, dataTableModel, actions);
     }
 
-    public static UI getUI() {
+    /**
+     * @param args
+     * @return
+     */
+    private static Main createSequencedMain(String[] args) {
+        SequenceUI ui = new SequenceUI();
+        Main main = new Main(ui);
+
+        Iterator<String> it = Arrays.asList(args).iterator();
+        it.next();  // --seq
+        // Setup
+        ui.choice("#" + it.next());  // Device.
+        ui.confirmNo();              // Prepare: no.
+        // Actions
+        try {
+            while (it.hasNext()) {
+                String op = it.next();
+                // Operation: Scan a single package
+                if (SCAN_PACKAGE_CMD.equals(op)) {
+                    System.out.println("Scanning package.");
+                    ui.action(ScanPackageAction.class);
+                    ui.client(it.next());
+                // Operation: Scan all packages
+                } else if (SCAN_ALL_CMD.equals(op)) {
+                    System.out.println("Scanning all packages.");
+                    ui.action(ScanAllPackagesAction.class);
+                // Operation: Export the output to a file
+                } else if (EXPORT_CMD.equals(op)) {
+                    System.out.println("Exporting data.");
+                    ui.action(ExportAction.class);
+                    ui.output(new File(it.next()));
+                // Operation: Import the input from a file or directory
+                } else if (IMPORT_CMD.equals(op)) {
+                    System.out.println("Importing data.");
+                    File file = new File(it.next());
+                    if (!file.exists()) {
+                        throw new RuntimeException(
+                                String.format("File does not exist, %s.", file.getAbsolutePath()));
+                    } else if (file.isFile()) {
+                        ui.action(ImportAction.class);
+                        ui.input(file);
+                    } else if (file.isDirectory()) {
+                        for (File content : file.listFiles()) {
+                            ui.action(ImportAction.class);
+                            ui.input(content);
+                        }
+                    }
+                // Operation: Compute preloaded classes with specific threshold
+                } else if (COMPUTE_FILE_CMD.equals(op)) {
+                    System.out.println("Compute preloaded classes.");
+                    ui.action(ComputeThresholdXAction.class);
+                    ui.input(it.next());
+                    ui.confirmYes();
+                    ui.output(new File(it.next()));
+                }
+            }
+        } catch (NoSuchElementException e) {
+            System.out.println("Failed to parse action sequence correctly.");
+            throw e;
+        }
+
+        return main;
+    }
+
+    public static IUI getUI() {
         return top.ui;
     }
 
@@ -176,6 +258,7 @@
         new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
 
         getUI().hideWaitDialog();
+        getUI().ready();
     }
 
     private void initDevice() {
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
index fbf83d2..5787d85 100644
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
@@ -16,6 +16,7 @@
 
 package com.android.preload.actions;
 
+import com.android.preload.Main;
 import java.awt.event.ActionEvent;
 
 import javax.swing.AbstractAction;
@@ -28,7 +29,11 @@
 
     @Override
     public void actionPerformed(ActionEvent e) {
-        new Thread(this).start();
+        if (Main.getUI().isSingleThreaded()) {
+            run();
+        } else {
+            new Thread(this).start();
+        }
     }
 
 }
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
index b524716..3a7f7f7 100644
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
@@ -32,14 +32,13 @@
 import java.util.regex.Pattern;
 
 import javax.swing.AbstractAction;
-import javax.swing.JFileChooser;
 
 /**
  * Compute an intersection of classes from the given data. A class is in the intersection if it
  * appears in at least the number of threshold given packages. An optional blacklist can be
  * used to filter classes from the intersection.
  */
-public class ComputeThresholdAction extends AbstractAction implements Runnable {
+public class ComputeThresholdAction extends AbstractThreadedAction {
     protected int threshold;
     private Pattern blacklist;
     private DumpTableModel dataTableModel;
@@ -72,7 +71,7 @@
             return;
         }
 
-        new Thread(this).start();
+        super.actionPerformed(e);
     }
 
     @Override
@@ -92,10 +91,8 @@
         boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
                 + " classes, would you like to save to disk?", "Save?");
         if (ret) {
-            JFileChooser jfc = new JFileChooser();
-            int ret2 = jfc.showSaveDialog(Main.getUI());
-            if (ret2 == JFileChooser.APPROVE_OPTION) {
-                File f = jfc.getSelectedFile();
+            File f = Main.getUI().showSaveDialog();
+            if (f != null) {
                 saveSet(result, f);
             }
         }
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
index cb8b3df..848a568 100644
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ExportAction.java
@@ -19,14 +19,11 @@
 import com.android.preload.DumpDataIO;
 import com.android.preload.DumpTableModel;
 import com.android.preload.Main;
-
 import java.awt.event.ActionEvent;
 import java.io.File;
 import java.io.PrintWriter;
 
-import javax.swing.AbstractAction;
-
-public class ExportAction extends AbstractAction implements Runnable {
+public class ExportAction extends AbstractThreadedAction {
     private File lastSaveFile;
     private DumpTableModel dataTableModel;
 
@@ -39,7 +36,7 @@
     public void actionPerformed(ActionEvent e) {
         lastSaveFile = Main.getUI().showSaveDialog();
         if (lastSaveFile != null) {
-            new Thread(this).start();
+            super.actionPerformed(e);
         }
     }
 
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
index 5c19765..bfeeb83 100644
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ImportAction.java
@@ -27,7 +27,7 @@
 
 import javax.swing.AbstractAction;
 
-public class ImportAction extends AbstractAction implements Runnable {
+public class ImportAction extends AbstractThreadedAction {
     private File[] lastOpenFiles;
     private DumpTableModel dataTableModel;
 
@@ -40,7 +40,7 @@
     public void actionPerformed(ActionEvent e) {
         lastOpenFiles = Main.getUI().showOpenDialog(true);
         if (lastOpenFiles != null) {
-            new Thread(this).start();
+            super.actionPerformed(e);
         }
     }
 
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
index 385e857..29464fc 100644
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
@@ -58,7 +58,12 @@
         if (packages.isEmpty()) {
             packages = DEFAULT_MONKEY_PACKAGES;
         }
-        new Thread(new RunMonkeyRunnable(packages)).start();
+        Runnable r = new RunMonkeyRunnable(packages);
+        if (Main.getUI().isSingleThreaded()) {
+            r.run();
+        } else {
+            new Thread(r).start();
+        }
     }
 
     private class RunMonkeyRunnable implements Runnable {
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
new file mode 100644
index 0000000..9371463
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/IUI.java
@@ -0,0 +1,45 @@
+package com.android.preload.ui;
+
+import com.android.ddmlib.Client;
+import java.io.File;
+import java.util.List;
+import javax.swing.Action;
+import javax.swing.ListModel;
+import javax.swing.table.TableModel;
+
+/**
+ * UI abstraction for the tool. This allows a graphical mode, command line mode,
+ * or silent mode.
+ */
+public interface IUI {
+
+    void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+            List<Action> actions);
+
+    void ready();
+
+    boolean isSingleThreaded();
+
+    Client getSelectedClient();
+
+    int getSelectedDataTableRow();
+
+    void showWaitDialog();
+
+    void updateWaitDialog(String s);
+
+    void hideWaitDialog();
+
+    void showMessageDialog(String s);
+
+    boolean showConfirmDialog(String title, String message);
+
+    String showInputDialog(String message);
+
+    <T> T showChoiceDialog(String title, String message, T[] choices);
+
+    File showSaveDialog();
+
+    File[] showOpenDialog(boolean multi);
+
+}
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
new file mode 100644
index 0000000..dc6a4f3
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
@@ -0,0 +1,222 @@
+package com.android.preload.ui;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import javax.swing.Action;
+import javax.swing.ListModel;
+import javax.swing.table.TableModel;
+
+public class SequenceUI implements IUI {
+
+    private ListModel<Client> clientListModel;
+    @SuppressWarnings("unused")
+    private TableModel dataTableModel;
+    private List<Action> actions;
+
+    private List<Object> sequence = new LinkedList<>();
+
+    public SequenceUI() {
+    }
+
+    @Override
+    public boolean isSingleThreaded() {
+        return true;
+    }
+
+    @Override
+    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+            List<Action> actions) {
+        this.clientListModel = clientListModel;
+        this.dataTableModel = dataTableModel;
+        this.actions = actions;
+    }
+
+    public SequenceUI action(Action a) {
+        sequence.add(a);
+        return this;
+    }
+
+    public SequenceUI action(Class<? extends Action> actionClass) {
+        for (Action a : actions) {
+            if (actionClass.equals(a.getClass())) {
+                sequence.add(a);
+                return this;
+            }
+        }
+        throw new IllegalArgumentException("No action of class " + actionClass + " found.");
+    }
+
+    public SequenceUI confirmYes() {
+        sequence.add(Boolean.TRUE);
+        return this;
+    }
+
+    public SequenceUI confirmNo() {
+        sequence.add(Boolean.FALSE);
+        return this;
+    }
+
+    public SequenceUI input(String input) {
+        sequence.add(input);
+        return this;
+    }
+
+    public SequenceUI input(File... f) {
+        sequence.add(f);
+        return this;
+    }
+
+    public SequenceUI output(File f) {
+        sequence.add(f);
+        return this;
+    }
+
+    public SequenceUI tableRow(int i) {
+        sequence.add(i);
+        return this;
+    }
+
+    private class ClientSelector {
+        private String pkg;
+
+        public ClientSelector(String pkg) {
+            this.pkg = pkg;
+        }
+
+        public Client getClient() {
+            for (int i = 0; i < clientListModel.getSize(); i++) {
+                ClientData cd = clientListModel.getElementAt(i).getClientData();
+                if (cd != null) {
+                    String s = cd.getClientDescription();
+                    if (pkg.equals(s)) {
+                        return clientListModel.getElementAt(i);
+                    }
+                }
+            }
+            throw new RuntimeException("Didn't find client " + pkg);
+        }
+    }
+
+    public SequenceUI client(String pkg) {
+        sequence.add(new ClientSelector(pkg));
+        return this;
+    }
+
+    public SequenceUI choice(String pattern) {
+        sequence.add(pattern);
+        return this;
+    }
+
+    @Override
+    public void ready() {
+        // Run the actions.
+        // No iterator or foreach loop as the sequence will be emptied while running.
+        try {
+            while (!sequence.isEmpty()) {
+                Object next = sequence.remove(0);
+                if (next instanceof Action) {
+                    ((Action)next).actionPerformed(null);
+                } else {
+                    throw new IllegalStateException("Didn't expect a non-action: " + next);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+
+        // Now shut down.
+        System.exit(0);
+    }
+
+    @Override
+    public Client getSelectedClient() {
+        Object next = sequence.remove(0);
+        if (next instanceof ClientSelector) {
+            return ((ClientSelector)next).getClient();
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public int getSelectedDataTableRow() {
+        Object next = sequence.remove(0);
+        if (next instanceof Integer) {
+            return ((Integer)next).intValue();
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public void showWaitDialog() {
+    }
+
+    @Override
+    public void updateWaitDialog(String s) {
+        System.out.println(s);
+    }
+
+    @Override
+    public void hideWaitDialog() {
+    }
+
+    @Override
+    public void showMessageDialog(String s) {
+        System.out.println(s);
+    }
+
+    @Override
+    public boolean showConfirmDialog(String title, String message) {
+        Object next = sequence.remove(0);
+        if (next instanceof Boolean) {
+            return ((Boolean)next).booleanValue();
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public String showInputDialog(String message) {
+        Object next = sequence.remove(0);
+        if (next instanceof String) {
+            return (String)next;
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public <T> T showChoiceDialog(String title, String message, T[] choices) {
+        Object next = sequence.remove(0);
+        if (next instanceof String) {
+            String s = (String)next;
+            for (T t : choices) {
+                if (t.toString().contains(s)) {
+                    return t;
+                }
+            }
+            return null;
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public File showSaveDialog() {
+        Object next = sequence.remove(0);
+        if (next instanceof File) {
+            System.out.println(next);
+            return (File)next;
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+    @Override
+    public File[] showOpenDialog(boolean multi) {
+        Object next = sequence.remove(0);
+        if (next instanceof File[]) {
+            return (File[])next;
+        }
+        throw new IllegalStateException("Unexpected: " + next);
+    }
+
+}
diff --git a/tools/preload2/src/com/android/preload/ui/UI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
similarity index 93%
rename from tools/preload2/src/com/android/preload/ui/UI.java
rename to tools/preload2/src/com/android/preload/ui/SwingUI.java
index 47174dd..cab3744 100644
--- a/tools/preload2/src/com/android/preload/ui/UI.java
+++ b/tools/preload2/src/com/android/preload/ui/SwingUI.java
@@ -41,7 +41,7 @@
 import javax.swing.SwingUtilities;
 import javax.swing.table.TableModel;
 
-public class UI extends JFrame {
+public class SwingUI extends JFrame implements IUI {
 
     private JList<Client> clientList;
     private JTable dataTable;
@@ -49,11 +49,18 @@
     // Shared file chooser, means the directory is retained.
     private JFileChooser jfc;
 
-    public UI(ListModel<Client> clientListModel,
-              TableModel dataTableModel,
-              List<Action> actions) {
+    public SwingUI() {
         super("Preloaded-classes computation");
+    }
 
+    @Override
+    public boolean isSingleThreaded() {
+        return false;
+    }
+
+    @Override
+    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+            List<Action> actions) {
         getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
                 BorderLayout.WEST);
         clientList.setCellRenderer(new ClientListCellRenderer());
@@ -74,18 +81,27 @@
 
         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         setBounds(100, 100, 800, 600);
+
+        setVisible(true);
     }
 
+    @Override
+    public void ready() {
+    }
+
+    @Override
     public Client getSelectedClient() {
         return clientList.getSelectedValue();
     }
 
+    @Override
     public int getSelectedDataTableRow() {
         return dataTable.getSelectedRow();
     }
 
     private JDialog currentWaitDialog = null;
 
+    @Override
     public void showWaitDialog() {
         if (currentWaitDialog == null) {
             currentWaitDialog = new JDialog(this, "Please wait...", true);
@@ -111,6 +127,7 @@
         });
     }
 
+    @Override
     public void updateWaitDialog(String s) {
         if (currentWaitDialog != null) {
             ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
@@ -124,6 +141,7 @@
         }
     }
 
+    @Override
     public void hideWaitDialog() {
         if (currentWaitDialog != null) {
             currentWaitDialog.setVisible(false);
@@ -131,6 +149,7 @@
         }
     }
 
+    @Override
     public void showMessageDialog(String s) {
         // Hide the wait dialog...
         if (currentWaitDialog != null) {
@@ -147,6 +166,7 @@
         }
     }
 
+    @Override
     public boolean showConfirmDialog(String title, String message) {
         // Hide the wait dialog...
         if (currentWaitDialog != null) {
@@ -164,6 +184,7 @@
         }
     }
 
+    @Override
     public String showInputDialog(String message) {
         // Hide the wait dialog...
         if (currentWaitDialog != null) {
@@ -180,6 +201,7 @@
         }
     }
 
+    @Override
     @SuppressWarnings("unchecked")
     public <T> T showChoiceDialog(String title, String message, T[] choices) {
         // Hide the wait dialog...
@@ -203,6 +225,7 @@
         }
     }
 
+    @Override
     public File showSaveDialog() {
         // Hide the wait dialog...
         if (currentWaitDialog != null) {
@@ -228,6 +251,7 @@
         }
     }
 
+    @Override
     public File[] showOpenDialog(boolean multi) {
         // Hide the wait dialog...
         if (currentWaitDialog != null) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 24cd275..82d41e3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -685,12 +685,22 @@
     /**
      * @hide
      * A hint about whether or not the network represented by this WifiConfiguration
-     * is metered.
+     * is metered. This is hinted at via the meteredHint bit on DHCP results set in
+     * {@link com.android.server.wifi.WifiStateMachine}, or via a network score in
+     * {@link com.android.server.wifi.ExternalScoreEvaluator}.
      */
     public boolean meteredHint;
 
     /**
      * @hide
+     * Indicates if a user has specified the WifiConfiguration to be metered. Users
+     * can toggle if a network is metered within Settings -> Data Usage -> Network
+     * Restrictions.
+     */
+    public boolean meteredOverride;
+
+    /**
+     * @hide
      * Setting this value will force scan results associated with this configuration to
      * be included in the bucket of networks that are externally scored.
      * If not set, associated scan results will be treated as legacy saved networks and
@@ -1367,6 +1377,7 @@
         didSelfAdd = false;
         ephemeral = false;
         meteredHint = false;
+        meteredOverride = false;
         useExternalScores = false;
         validatedInternetAccess = false;
         mIpConfiguration = new IpConfiguration();
@@ -1470,9 +1481,11 @@
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
         if (this.ephemeral) sbuf.append(" ephemeral");
         if (this.meteredHint) sbuf.append(" meteredHint");
+        if (this.meteredOverride) sbuf.append(" meteredOverride");
         if (this.useExternalScores) sbuf.append(" useExternalScores");
         if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
-            || this.ephemeral || this.meteredHint || this.useExternalScores) {
+            || this.ephemeral || this.meteredHint || this.meteredOverride
+            || this.useExternalScores) {
             sbuf.append("\n");
         }
         sbuf.append(" KeyMgmt:");
@@ -1897,6 +1910,7 @@
             validatedInternetAccess = source.validatedInternetAccess;
             ephemeral = source.ephemeral;
             meteredHint = source.meteredHint;
+            meteredOverride = source.meteredOverride;
             useExternalScores = source.useExternalScores;
             if (source.visibility != null) {
                 visibility = new Visibility(source.visibility);
@@ -1978,6 +1992,7 @@
         dest.writeInt(validatedInternetAccess ? 1 : 0);
         dest.writeInt(ephemeral ? 1 : 0);
         dest.writeInt(meteredHint ? 1 : 0);
+        dest.writeInt(meteredOverride ? 1 : 0);
         dest.writeInt(useExternalScores ? 1 : 0);
         dest.writeInt(creatorUid);
         dest.writeInt(lastConnectUid);
@@ -2049,6 +2064,7 @@
                 config.validatedInternetAccess = in.readInt() != 0;
                 config.ephemeral = in.readInt() != 0;
                 config.meteredHint = in.readInt() != 0;
+                config.meteredOverride = in.readInt() != 0;
                 config.useExternalScores = in.readInt() != 0;
                 config.creatorUid = in.readInt();
                 config.lastConnectUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
index 4aacbae..4b21b15 100644
--- a/wifi/java/android/net/wifi/aware/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -22,7 +22,7 @@
 /**
  * Defines a request object to configure a Wi-Fi Aware network. Built using
  * {@link ConfigRequest.Builder}. Configuration is requested using
- * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}.
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}.
  * Note that the actual achieved configuration may be different from the
  * requested configuration - since different applications may request different
  * configurations.
diff --git a/wifi/java/android/net/wifi/aware/LvBufferUtils.java b/wifi/java/android/net/wifi/aware/LvBufferUtils.java
deleted file mode 100644
index 3265243..0000000
--- a/wifi/java/android/net/wifi/aware/LvBufferUtils.java
+++ /dev/null
@@ -1,340 +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.aware;
-
-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_AWARE_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/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index ba493a0..3925bd7 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Defines the configuration of a Aware publish session. Built using
@@ -84,7 +85,8 @@
     /** @hide */
     public final boolean mEnableTerminateNotification;
 
-    private PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+    /** @hide */
+    public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
             int publishType, int publichCount, int ttlSec, boolean enableTerminateNotification) {
         mServiceName = serviceName;
         mServiceSpecificInfo = serviceSpecificInfo;
@@ -99,9 +101,9 @@
     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="
+                + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
+                mMatchFilter)).toString() + ", mPublishType=" + mPublishType + ", mPublishCount="
+                + mPublishCount + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
                 + mEnableTerminateNotification + "]";
     }
 
@@ -186,7 +188,7 @@
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
-        if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+        if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid txFilter configuration - LV fields do not match up to length");
         }
@@ -281,18 +283,17 @@
          * 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.
+         * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
+         *                    array).
          *
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setMatchFilter(@Nullable byte[] matchFilter) {
-            mMatchFilter = matchFilter;
+        public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
+            mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
+                    matchFilter).getArray();
             return this;
         }
 
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 5e14f8f..bf35445 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Defines the configuration of a Aware subscribe session. Built using
@@ -106,7 +107,8 @@
     /** @hide */
     public final boolean mEnableTerminateNotification;
 
-    private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+    /** @hide */
+    public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
             int subscribeType, int publichCount, int ttlSec, int matchStyle,
             boolean enableTerminateNotification) {
         mServiceName = serviceName;
@@ -123,10 +125,11 @@
     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 + "]";
+                + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
+                mMatchFilter)).toString() + ", mSubscribeType=" + mSubscribeType
+                + ", mSubscribeCount=" + mSubscribeCount + ", mTtlSec=" + mTtlSec + ", mMatchType="
+                + mMatchStyle + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+                + "]";
     }
 
     @Override
@@ -213,7 +216,7 @@
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
-        if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+        if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid matchFilter configuration - LV fields do not match up to length");
         }
@@ -313,18 +316,17 @@
          * 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.
+         * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
+         *                    array).
          *
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setMatchFilter(@Nullable byte[] matchFilter) {
-            mMatchFilter = matchFilter;
+        public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
+            mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
+                    matchFilter).getArray();
             return this;
         }
 
diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index 56c9069..29f10e9 100644
--- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -22,8 +22,10 @@
 
 import java.nio.BufferOverflowException;
 import java.nio.ByteOrder;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
@@ -32,7 +34,7 @@
  * the Type field and the Length field. A Type field size of 0 is allowed -
  * allowing usage for LV (no T) array formats.
  *
- * @hide PROPOSED_AWARE_API
+ * @hide
  */
 public class TlvBufferUtils {
     private TlvBufferUtils() {
@@ -111,6 +113,31 @@
         }
 
         /**
+         * Creates a TLV array (of the previously specified Type and Length sizes) from the input
+         * list. Allocates an array matching the contents (and required Type and Length
+         * fields), copies the contents, and set the Length fields. The Type field is set to 0.
+         *
+         * @param list A list of fields to be added to the TLV buffer.
+         * @return The constructor of the TLV.
+         */
+        public TlvConstructor allocateAndPut(@Nullable List<byte[]> list) {
+            if (list != null) {
+                int size = 0;
+                for (byte[] field : list) {
+                    size += mTypeSize + mLengthSize;
+                    if (field != null) {
+                        size += field.length;
+                    }
+                }
+                allocate(size);
+                for (byte[] field : list) {
+                    putByteArray(0, field);
+                }
+            }
+            return this;
+        }
+
+        /**
          * Copies a byte into the TLV with the indicated type. For an LV
          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
          * TlvConstructor(int, int)} ) the type field is ignored.
@@ -319,6 +346,10 @@
             this.length = length;
             this.refArray = refArray;
             this.offset = offset;
+
+            if (offset + length > refArray.length) {
+                throw new BufferOverflowException();
+            }
         }
 
         /**
@@ -393,7 +424,7 @@
          * @param typeSize Number of bytes used for the Type (T) field. Valid
          *            values are 0 (i.e. indicating the format is LV rather than
          *            TLV), 1, and 2 bytes.
-         * @param lengthSize Number of bytes sued for the Length (L) field.
+         * @param lengthSize Number of bytes used for the Length (L) field.
          *            Values values are 1 or 2 bytes.
          * @param array The TLV formatted byte-array to parse.
          */
@@ -450,6 +481,18 @@
         }
 
         /**
+         * Returns a List with the raw contents (no types) of the iterator.
+         */
+        public List<byte[]> toList() {
+            List<byte[]> list = new ArrayList<>();
+            for (TlvElement tlv : this) {
+                list.add(Arrays.copyOfRange(tlv.refArray, tlv.offset, tlv.offset + tlv.length));
+            }
+
+            return list;
+        }
+
+        /**
          * Returns an iterator to step through a TLV formatted byte-array. The
          * individual elements returned by the iterator are {@link TlvElement}.
          */
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
index 072ccab..95d128d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
@@ -69,8 +69,9 @@
 
     /**
      * Returns the maximum length of byte array that can be used to specify a Aware match filter.
-     * Restricts the parameters of the {@link PublishConfig.Builder#setMatchFilter(byte[])} and
-     * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}.
+     * Restricts the parameters of the
+     * {@link PublishConfig.Builder#setMatchFilter(java.util.List<byte[]>)} and
+     * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List<byte[]>)}.
      *
      * @return A positive integer, maximum legngth of byte array for Aware discovery match filter.
      */
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
index e8335d1..451d8a5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
@@ -35,7 +35,7 @@
  *     <li>Sending messages: {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[])} or
  *     {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)} methods.
  *     <li>Creating a network-specifier when requesting a Aware connection:
- *     {@link #createNetworkSpecifier(int, WifiAwareManager.PeerHandle, byte[])}.
+ *     {@link #createNetworkSpecifier(WifiAwareManager.PeerHandle, byte[])}.
  * </ul>
  * The {@link #destroy()} method must be called to destroy discovery sessions once they are
  * no longer needed.
@@ -140,7 +140,7 @@
      * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} event.
+     * byte[], java.util.List<byte[]>)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
      *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
@@ -154,7 +154,7 @@
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} or
+     * byte[], java.util.List<byte[]>)} or
      * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
      * byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
@@ -187,7 +187,7 @@
      * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} event.
+     * byte[], java.util.List<byte[]>)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
      *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
@@ -203,7 +203,7 @@
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} or
+     * byte[], java.util.List<byte[]>)} or
      * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
      * byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
@@ -220,7 +220,7 @@
     /**
      * Start a ranging operation with the specified peers. The peer IDs are obtained from an
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} or
+     * byte[], java.util.List<byte[]>)} or
      * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
      * byte[])} operation - can
      * only range devices which are part of an ongoing discovery session.
@@ -260,13 +260,13 @@
      * OOB (out-of-band) mechanism then use the alternative
      * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
      * peer's MAC address.
+     * <p>
+     * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+     * and a Publisher is a RESPONDER.
      *
-     * @param role The role of this device:
-     * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
-     * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peerHandle The peer's handle obtained through
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} or
+     * byte[], java.util.List<byte[]>)} or
      * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
      * 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 null - indicating that
@@ -283,8 +283,8 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
-            @Nullable WifiAwareManager.PeerHandle peerHandle, @Nullable byte[] token) {
+    public String createNetworkSpecifier(@Nullable WifiAwareManager.PeerHandle peerHandle,
+            @Nullable byte[] token) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifier: called on terminated session");
             return null;
@@ -295,6 +295,10 @@
                 return null;
             }
 
+            int role = this instanceof WifiAwareSubscribeDiscoverySession
+                    ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                    : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
             return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token);
         }
     }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
index 6331c9c..fdf0d01 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
@@ -21,6 +21,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Base class for Aware session events callbacks. Should be extended by
@@ -130,11 +131,10 @@
      * @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 service discovery.
+     * @param matchFilter The filter which resulted in this service discovery.
      */
     public void onServiceDiscovered(WifiAwareManager.PeerHandle peerHandle,
-            byte[] serviceSpecificInfo, byte[] matchFilter) {
+            byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
         /* empty */
     }
 
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index a34ef47..029794d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -45,7 +45,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.nio.BufferOverflowException;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * This class provides the primary API for managing Wi-Fi Aware operations:
@@ -63,7 +65,7 @@
  * <li>Create a Aware network specifier to be used with
  * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
  * to set-up a Aware connection with a peer. Refer to
- * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, PeerHandle, byte[])} and
+ * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])} and
  * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
  * </ul>
  * <p>
@@ -114,7 +116,7 @@
  *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
  *        <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
  *        {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} or
- *        {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, PeerHandle, byte[])}.
+ *        {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])}.
  *    </ul>
  *
  * @hide PROPOSED_AWARE_API
@@ -224,7 +226,7 @@
      * Connection creation role is that of INITIATOR. Used to create a network specifier string
      * when requesting a Aware network.
      *
-     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, PeerHandle, byte[])
+     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])
      * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
@@ -233,7 +235,7 @@
      * Connection creation role is that of RESPONDER. Used to create a network specifier string
      * when requesting a Aware network.
      *
-     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, PeerHandle, byte[])
+     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])
      * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
@@ -874,12 +876,22 @@
                         case CALLBACK_SESSION_TERMINATED:
                             onProxySessionTerminated(msg.arg1);
                             break;
-                        case CALLBACK_MATCH:
-                            mOriginalCallback.onServiceDiscovered(
-                                    new PeerHandle(msg.arg1),
+                        case CALLBACK_MATCH: {
+                            List<byte[]> matchFilter = null;
+                            byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
+                            try {
+                                matchFilter = new TlvBufferUtils.TlvIterable(0, 1, arg).toList();
+                            } catch (BufferOverflowException e) {
+                                matchFilter = null;
+                                Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '"
+                                        + new String(HexEncoding.encode(arg))
+                                        + "' - cannot be parsed: e=" + e);
+                            }
+                            mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
                                     msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2));
+                                    matchFilter);
                             break;
+                        }
                         case CALLBACK_MESSAGE_SEND_SUCCESS:
                             mOriginalCallback.onMessageSendSucceeded(msg.arg1);
                             break;
@@ -966,7 +978,7 @@
         @Override
         public void onMessageReceived(int peerId, byte[] message) {
             if (VDBG) {
-                Log.v(TAG, "onMessageReceived: peerId='" + peerId);
+                Log.v(TAG, "onMessageReceived: peerId=" + peerId);
             }
 
             Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED);
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index acb60a4..005895a 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -193,8 +193,8 @@
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
      *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
      *     when using Aware discovery use the alternative network specifier method -
-     *     {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int,
-     *     WifiAwareManager.PeerHandle, byte[])}.
+     *     {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(WifiAwareManager.PeerHandle,
+     *     byte[])}.
      *
      * @param role  The role of this device:
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
@@ -211,7 +211,7 @@
      *
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
-     * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest,
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
new file mode 100644
index 0000000..15641ab
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -0,0 +1,282 @@
+/*
+ * 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.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+import java.nio.BufferOverflowException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test harness for TlvBufferUtils class.
+ */
+@SmallTest
+public class TlvBufferUtilsTest {
+    @Rule
+    public ErrorCollector collector = new ErrorCollector();
+
+    /*
+     * TlvBufferUtils Tests
+     */
+
+    @Test
+    public void testTlvBuild() {
+        TlvBufferUtils.TlvConstructor tlv11 = new TlvBufferUtils.TlvConstructor(1, 1);
+        tlv11.allocate(15);
+        tlv11.putByte(0, (byte) 2);
+        tlv11.putByteArray(2, new byte[] {
+                0, 1, 2 });
+
+        collector.checkThat("tlv11-correct-construction",
+                tlv11.getArray(), equalTo(new byte[]{0, 1, 2, 2, 3, 0, 1, 2}));
+
+        TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+        tlv01.allocate(15);
+        tlv01.putByte(0, (byte) 2);
+        tlv01.putByteArray(2, new byte[] {
+                0, 1, 2 });
+
+        collector.checkThat("tlv01-correct-construction",
+                tlv01.getArray(), equalTo(new byte[] {1, 2, 3, 0, 1, 2 }));
+
+        collector.checkThat("tlv11-valid",
+                TlvBufferUtils.isValid(tlv11.getArray(), 1, 1),
+                equalTo(true));
+        collector.checkThat("tlv01-valid",
+                TlvBufferUtils.isValid(tlv01.getArray(), 0, 1),
+                equalTo(true));
+    }
+
+    /**
+     * Verify that can build a valid TLV from a List of byte[].
+     */
+    @Test
+    public void testTlvListOperations() {
+        byte[] entry1 = { 1, 2, 3 };
+        byte[] entry2 = { 4, 5 };
+        byte[] entry3 = new byte[0];
+        List<byte[]> data = new ArrayList<>();
+        data.add(entry1);
+        data.add(entry2);
+        data.add(entry3);
+        data.add(null); // zero-length should work
+
+        TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+        tlv01.allocateAndPut(data);
+        byte[] tlvData = tlv01.getArray();
+        List<byte[]> parsedList = new TlvBufferUtils.TlvIterable(0, 1, tlvData).toList();
+
+        collector.checkThat("tlvData-correct-length", tlvData.length,
+                equalTo(entry1.length + 1 + entry2.length + 1 + entry3.length + 1 + 1));
+        collector.checkThat("parsedList-correct-length", parsedList.size(), equalTo(4));
+        collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(entry1));
+        collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(entry2));
+        collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(entry3));
+        collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0]));
+    }
+
+    /**
+     * Verify that can parse a (correctly formatted) byte array to a list.
+     */
+    @Test
+    public void testTlvParseToList() {
+        byte[] validTlv01 = { 0, 1, 55, 2, 33, 66, 0 };
+
+        List<byte[]> parsedList = new TlvBufferUtils.TlvIterable(0, 1, validTlv01).toList();
+
+        collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(new byte[0]));
+        collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(new byte[] { 55 }));
+        collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(new byte[] { 33, 66 }));
+        collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0]));
+    }
+
+    /**
+     * Verify that an exception is thrown when trying to parse an invalid array.
+     */
+    @Test(expected = BufferOverflowException.class)
+    public void testTlvParseToListError() {
+        byte[] invalidTlv01 = { 0, 1, 55, 2, 55, 66, 3 }; // bad data
+
+        List<byte[]> data = new TlvBufferUtils.TlvIterable(0, 1, invalidTlv01).toList();
+    }
+
+    @Test
+    public void testTlvIterate() {
+        final String ascii = "ABC";
+        final String nonAscii = "何かもっと複雑な";
+
+        TlvBufferUtils.TlvConstructor tlv22 = new TlvBufferUtils.TlvConstructor(2, 2);
+        tlv22.allocate(18);
+        tlv22.putInt(0, 2);
+        tlv22.putShort(2, (short) 3);
+        tlv22.putZeroLengthElement(55);
+
+        TlvBufferUtils.TlvIterable tlv22It = new TlvBufferUtils.TlvIterable(2, 2, tlv22.getArray());
+        int count = 0;
+        for (TlvBufferUtils.TlvElement tlv : tlv22It) {
+            if (count == 0) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(0));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(4));
+                collector.checkThat("tlv22-correct-iteration-DATA", tlv.getInt(), equalTo(2));
+            } else if (count == 1) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(2));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(2));
+                collector.checkThat("tlv22-correct-iteration-DATA", (int) tlv.getShort(),
+                        equalTo(3));
+            } else if (count == 2) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(55));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(0));
+            } else {
+                collector.checkThat("Invalid number of iterations in loop - tlv22", true,
+                        equalTo(false));
+            }
+            ++count;
+        }
+        if (count != 3) {
+            collector.checkThat("Invalid number of iterations outside loop - tlv22", true,
+                    equalTo(false));
+        }
+
+        TlvBufferUtils.TlvConstructor tlv02 = new TlvBufferUtils.TlvConstructor(0, 2);
+        tlv02.allocate(100);
+        tlv02.putByte(0, (byte) 2);
+        tlv02.putString(0, ascii);
+        tlv02.putString(0, nonAscii);
+
+        TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray());
+        count = 0;
+        for (TlvBufferUtils.TlvElement tlv : tlv02It) {
+            if (count == 0) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length, equalTo(1));
+                collector.checkThat("tlv02-correct-iteration-DATA", (int) tlv.getByte(),
+                        equalTo(2));
+            } else if (count == 1) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+                        equalTo(ascii.length()));
+                collector.checkThat("tlv02-correct-iteration-DATA", tlv.getString().equals(ascii),
+                        equalTo(true));
+            } else if (count == 2) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+                        equalTo(nonAscii.getBytes().length));
+                collector.checkThat("tlv02-correct-iteration-DATA",
+                        tlv.getString().equals(nonAscii), equalTo(true));
+            } else {
+                collector.checkThat("Invalid number of iterations in loop - tlv02", true,
+                        equalTo(false));
+            }
+            ++count;
+        }
+        collector.checkThat("Invalid number of iterations outside loop - tlv02", count,
+                equalTo(3));
+
+        collector.checkThat("tlv22-valid",
+                TlvBufferUtils.isValid(tlv22.getArray(), 2, 2),
+                equalTo(true));
+        collector.checkThat("tlv02-valid",
+                TlvBufferUtils.isValid(tlv02.getArray(), 0, 2),
+                equalTo(true));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1L0() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeTm3L2() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(-3, 2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1Lm2() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, -2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1L3() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 3);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT3L1() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(3, 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1L0() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 0, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeTm3L2() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(-3, 2, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1Lm2() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, -2, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1L3() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 3, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT3L1() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(3, 1, dummy);
+    }
+
+    /**
+     * Validate that a malformed byte array fails the TLV validity test.
+     */
+    @Test
+    public void testTlvInvalidByteArray() {
+        TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+        tlv01.allocate(15);
+        tlv01.putByte(0, (byte) 2);
+        tlv01.putByteArray(2, new byte[]{0, 1, 2});
+
+        byte[] array = tlv01.getArray();
+        array[0] = 10;
+
+        collector.checkThat("tlv01-invalid",
+                TlvBufferUtils.isValid(array, 0, 1), equalTo(false));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
new file mode 100644
index 0000000..24c0127
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -0,0 +1,1063 @@
+/*
+ * 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.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.RttManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import libcore.util.HexEncoding;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Unit test harness for WifiAwareManager class.
+ */
+@SmallTest
+public class WifiAwareManagerTest {
+    private WifiAwareManager mDut;
+    private TestLooper mMockLooper;
+    private Handler mMockLooperHandler;
+
+    @Rule
+    public ErrorCollector collector = new ErrorCollector();
+
+    @Mock
+    public Context mockContext;
+
+    @Mock
+    public WifiAwareAttachCallback mockCallback;
+
+    @Mock
+    public WifiAwareDiscoverySessionCallback mockSessionCallback;
+
+    @Mock
+    public IWifiAwareManager mockAwareService;
+
+    @Mock
+    public WifiAwarePublishDiscoverySession mockPublishSession;
+
+    @Mock
+    public WifiAwareSubscribeDiscoverySession mockSubscribeSession;
+
+    @Mock
+    public RttManager.RttListener mockRttListener;
+
+    private static final int AWARE_STATUS_ERROR = -1;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mDut = new WifiAwareManager(mockContext, mockAwareService);
+        mMockLooper = new TestLooper();
+        mMockLooperHandler = new Handler(mMockLooper.getLooper());
+    }
+
+    /*
+     * Straight pass-through tests
+     */
+
+    /**
+     * Validate pass-through of enableUsage() API.
+     */
+    @Test
+    public void testEnableUsage() throws Exception {
+        mDut.enableUsage();
+
+        verify(mockAwareService).enableUsage();
+    }
+
+    /**
+     * Validate pass-through of disableUsage() API.
+     */
+    @Test
+    public void testDisableUsage() throws Exception {
+        mDut.disableUsage();
+
+        verify(mockAwareService).disableUsage();
+    }
+
+    /**
+     * Validate pass-through of isUsageEnabled() API.
+     */
+    @Test
+    public void testIsUsageEnable() throws Exception {
+        mDut.isAvailable();
+
+        verify(mockAwareService).isUsageEnabled();
+    }
+
+    /**
+     * Validate pass-through of getCharacteristics() API.
+     */
+    @Test
+    public void testGetCharacteristics() throws Exception {
+        mDut.getCharacteristics();
+
+        verify(mockAwareService).getCharacteristics();
+    }
+
+    /*
+     * WifiAwareEventCallbackProxy Tests
+     */
+
+    /**
+     * Validate the successful connect flow: (1) connect + success (2) publish, (3) disconnect
+     * (4) try publishing on old session (5) connect again
+     */
+    @Test
+    public void testConnectFlow() throws Exception {
+        final int clientId = 4565;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IBinder> binder = ArgumentCaptor.forClass(IBinder.class);
+
+        // (1) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish - should succeed
+        PublishConfig publishConfig = new PublishConfig.Builder().build();
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                any(IWifiAwareDiscoverySessionCallback.class));
+
+        // (3) disconnect
+        session.destroy();
+        inOrder.verify(mockAwareService).disconnect(eq(clientId), eq(binder.getValue()));
+
+        // (4) try publishing again - fails silently
+        session.publish(new PublishConfig.Builder().build(), mockSessionCallback,
+                mMockLooperHandler);
+
+        // (5) connect
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+                any(IWifiAwareEventCallback.class), (ConfigRequest) isNull(), eq(false));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /**
+     * Validate the failed connect flow: (1) connect + failure, (2) connect + success (3) subscribe
+     */
+    @Test
+    public void testConnectFailure() throws Exception {
+        final int clientId = 4565;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        // (1) connect + failure
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttachFailed();
+
+        // (2) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (4) subscribe: should succeed
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                any(IWifiAwareDiscoverySessionCallback.class));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /**
+     * Validate that can call connect to create multiple sessions: (1) connect
+     * + success, (2) try connect again
+     */
+    @Test
+    public void testInvalidConnectSequence() throws Exception {
+        final int clientId = 4565;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        // (1) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+        // (2) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId + 1);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /*
+     * WifiAwareDiscoverySessionCallbackProxy Tests
+     */
+
+    /**
+     * Validate the publish flow: (0) connect + success, (1) publish, (2)
+     * success creates session, (3) pass through everything, (4) update publish
+     * through session, (5) terminate locally, (6) try another command -
+     * ignored.
+     */
+    @Test
+    public void testPublishFlow() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+        final String string1 = "hey from here...";
+        final byte[] matchFilter = { 1, 12, 2, 31, 32 };
+        final int messageId = 2123;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+                WifiAwareManager.PeerHandle.class);
+        ArgumentCaptor<List<byte[]>> matchFilterCaptor = ArgumentCaptor.forClass(
+                (Class) List.class);
+
+        // (0) connect + success
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (1) publish
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+
+        // (2) publish session created
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) ...
+        publishSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+        sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+        sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+        sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+        sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+        mMockLooper.dispatchAll();
+
+        inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+                eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+        inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+                eq(string1.getBytes()),
+                matchFilterCaptor.capture());
+
+        // note: need to capture/compare elements since the Mockito eq() is a shallow comparator
+        List<byte[]> parsedMatchFilter = new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList();
+        collector.checkThat("match-filter-size", parsedMatchFilter.size(),
+                equalTo(matchFilterCaptor.getValue().size()));
+        collector.checkThat("match-filter-entry0", parsedMatchFilter.get(0),
+                equalTo(matchFilterCaptor.getValue().get(0)));
+        collector.checkThat("match-filter-entry1", parsedMatchFilter.get(1),
+                equalTo(matchFilterCaptor.getValue().get(1)));
+
+        assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+                eq(string1.getBytes()));
+        assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+        inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+        // (4) update publish
+        publishSession.getValue().updatePublish(publishConfig);
+        sessionProxyCallback.getValue().onSessionConfigFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).updatePublish(eq(clientId), eq(sessionId),
+                eq(publishConfig));
+        inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+        // (5) terminate
+        publishSession.getValue().destroy();
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+        // (6) try an update (nothing)
+        publishSession.getValue().updatePublish(publishConfig);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+    }
+
+    /**
+     * Validate race condition of session terminate and session action: (1)
+     * connect, (2) publish success + terminate, (3) update.
+     */
+    @Test
+    public void testPublishRemoteTerminate() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish: successfully - then terminated
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+        // (3) failure when trying to update: NOP
+        publishSession.getValue().updatePublish(publishConfig);
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+    }
+
+    /**
+     * Validate the subscribe flow: (0) connect + success, (1) subscribe, (2)
+     * success creates session, (3) pass through everything, (4) update
+     * subscribe through session, (5) terminate locally, (6) try another command
+     * - ignored.
+     */
+    @Test
+    public void testSubscribeFlow() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+        final String string1 = "hey from here...";
+        final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
+        final int messageId = 2123;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(WifiAwareSubscribeDiscoverySession.class);
+        ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+                WifiAwareManager.PeerHandle.class);
+
+        // (0) connect + success
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (1) subscribe
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                sessionProxyCallback.capture());
+
+        // (2) subscribe session created
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+
+        // (3) ...
+        subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+        sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+        sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+        sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+        sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+        mMockLooper.dispatchAll();
+
+        inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+                eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+        inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+                eq(string1.getBytes()), (List<byte[]>) isNull());
+        assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+                eq(string1.getBytes()));
+        assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+        inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+        // (4) update subscribe
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+        sessionProxyCallback.getValue().onSessionConfigFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).updateSubscribe(eq(clientId), eq(sessionId),
+                eq(subscribeConfig));
+        inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+        // (5) terminate
+        subscribeSession.getValue().destroy();
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+        // (6) try an update (nothing)
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+    }
+
+    /**
+     * Validate race condition of session terminate and session action: (1)
+     * connect, (2) subscribe success + terminate, (3) update.
+     */
+    @Test
+    public void testSubscribeRemoteTerminate() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(WifiAwareSubscribeDiscoverySession.class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) subscribe: successfully - then terminated
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+        // (3) failure when trying to update: NOP
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+    }
+
+    /*
+     * ConfigRequest Tests
+     */
+
+    @Test
+    public void testConfigRequestBuilderDefaults() {
+        ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+        collector.checkThat("mClusterHigh", ConfigRequest.CLUSTER_ID_MAX,
+                equalTo(configRequest.mClusterHigh));
+        collector.checkThat("mClusterLow", ConfigRequest.CLUSTER_ID_MIN,
+                equalTo(configRequest.mClusterLow));
+        collector.checkThat("mMasterPreference", 0,
+                equalTo(configRequest.mMasterPreference));
+        collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand));
+    }
+
+    @Test
+    public void testConfigRequestBuilder() {
+        final int clusterHigh = 100;
+        final int clusterLow = 5;
+        final int masterPreference = 55;
+        final boolean supportBand5g = true;
+
+        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+                .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+                .setSupport5gBand(supportBand5g)
+                .build();
+
+        collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
+        collector.checkThat("mClusterLow", clusterLow, equalTo(configRequest.mClusterLow));
+        collector.checkThat("mMasterPreference", masterPreference,
+                equalTo(configRequest.mMasterPreference));
+        collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefNegative() {
+        ConfigRequest.Builder builder = new ConfigRequest.Builder();
+        builder.setMasterPreference(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefReserved1() {
+        new ConfigRequest.Builder().setMasterPreference(1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefReserved255() {
+        new ConfigRequest.Builder().setMasterPreference(255);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefTooLarge() {
+        new ConfigRequest.Builder().setMasterPreference(256);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowNegative() {
+        new ConfigRequest.Builder().setClusterLow(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterHighNegative() {
+        new ConfigRequest.Builder().setClusterHigh(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowAboveMax() {
+        new ConfigRequest.Builder().setClusterLow(ConfigRequest.CLUSTER_ID_MAX + 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterHighAboveMax() {
+        new ConfigRequest.Builder().setClusterHigh(ConfigRequest.CLUSTER_ID_MAX + 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowLargerThanHigh() {
+        new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build();
+    }
+
+    @Test
+    public void testConfigRequestParcel() {
+        final int clusterHigh = 189;
+        final int clusterLow = 25;
+        final int masterPreference = 177;
+        final boolean supportBand5g = true;
+
+        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+                .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+                .setSupport5gBand(supportBand5g)
+                .build();
+
+        Parcel parcelW = Parcel.obtain();
+        configRequest.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        ConfigRequest rereadConfigRequest = ConfigRequest.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(configRequest, rereadConfigRequest);
+    }
+
+    /*
+     * SubscribeConfig Tests
+     */
+
+    @Test
+    public void testSubscribeConfigBuilderDefaults() {
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+        collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+        collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
+                equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
+        collector.checkThat("mSubscribeCount", subscribeConfig.mSubscribeCount, equalTo(0));
+        collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
+        collector.checkThat("mMatchStyle", subscribeConfig.mMatchStyle,
+                equalTo(SubscribeConfig.MATCH_STYLE_ALL));
+        collector.checkThat("mEnableTerminateNotification",
+                subscribeConfig.mEnableTerminateNotification, equalTo(true));
+    }
+
+    @Test
+    public void testSubscribeConfigBuilder() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = { 1, 16, 1, 22 };
+        final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+        final int subscribeCount = 10;
+        final int subscribeTtl = 15;
+        final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+        final boolean enableTerminateNotification = false;
+
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                .setSubscribeType(subscribeType)
+                .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        collector.checkThat("mServiceName", serviceName.getBytes(),
+                equalTo(subscribeConfig.mServiceName));
+        collector.checkThat("mServiceSpecificInfo",
+                serviceSpecificInfo.getBytes(), equalTo(subscribeConfig.mServiceSpecificInfo));
+        collector.checkThat("mMatchFilter", matchFilter, equalTo(subscribeConfig.mMatchFilter));
+        collector.checkThat("mSubscribeType", subscribeType,
+                equalTo(subscribeConfig.mSubscribeType));
+        collector.checkThat("mSubscribeCount", subscribeCount,
+                equalTo(subscribeConfig.mSubscribeCount));
+        collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
+        collector.checkThat("mMatchStyle", matchStyle, equalTo(subscribeConfig.mMatchStyle));
+        collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+                equalTo(subscribeConfig.mEnableTerminateNotification));
+    }
+
+    @Test
+    public void testSubscribeConfigParcel() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = { 1, 16, 1, 22 };
+        final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+        final int subscribeCount = 10;
+        final int subscribeTtl = 15;
+        final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+        final boolean enableTerminateNotification = true;
+
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                .setSubscribeType(subscribeType)
+                .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        Parcel parcelW = Parcel.obtain();
+        subscribeConfig.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        SubscribeConfig rereadSubscribeConfig = SubscribeConfig.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(subscribeConfig, rereadSubscribeConfig);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderBadSubscribeType() {
+        new SubscribeConfig.Builder().setSubscribeType(10);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderNegativeCount() {
+        new SubscribeConfig.Builder().setSubscribeCount(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderNegativeTtl() {
+        new SubscribeConfig.Builder().setTtlSec(-100);
+    }
+
+    /**
+     * Validate that a bad match style configuration throws an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderBadMatchStyle() {
+        new SubscribeConfig.Builder().setMatchStyle(10);
+    }
+
+    /*
+     * PublishConfig Tests
+     */
+
+    @Test
+    public void testPublishConfigBuilderDefaults() {
+        PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+        collector.checkThat("mPublishType", publishConfig.mPublishType,
+                equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
+        collector.checkThat("mPublishCount", publishConfig.mPublishCount, equalTo(0));
+        collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
+        collector.checkThat("mEnableTerminateNotification",
+                publishConfig.mEnableTerminateNotification, equalTo(true));
+    }
+
+    @Test
+    public void testPublishConfigBuilder() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = { 1, 16, 1, 22 };
+        final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+        final int publishCount = 10;
+        final int publishTtl = 15;
+        final boolean enableTerminateNotification = false;
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                .setPublishType(publishType)
+                .setPublishCount(publishCount).setTtlSec(publishTtl)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        collector.checkThat("mServiceName", serviceName.getBytes(),
+                equalTo(publishConfig.mServiceName));
+        collector.checkThat("mServiceSpecificInfo",
+                serviceSpecificInfo.getBytes(), equalTo(publishConfig.mServiceSpecificInfo));
+        collector.checkThat("mMatchFilter", matchFilter, equalTo(publishConfig.mMatchFilter));
+        collector.checkThat("mPublishType", publishType, equalTo(publishConfig.mPublishType));
+        collector.checkThat("mPublishCount", publishCount, equalTo(publishConfig.mPublishCount));
+        collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
+        collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+                equalTo(publishConfig.mEnableTerminateNotification));
+    }
+
+    @Test
+    public void testPublishConfigParcel() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = { 1, 16, 1, 22 };
+        final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+        final int publishCount = 10;
+        final int publishTtl = 15;
+        final boolean enableTerminateNotification = false;
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                .setPublishType(publishType)
+                .setPublishCount(publishCount).setTtlSec(publishTtl)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        Parcel parcelW = Parcel.obtain();
+        publishConfig.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        PublishConfig rereadPublishConfig = PublishConfig.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(publishConfig, rereadPublishConfig);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderBadPublishType() {
+        new PublishConfig.Builder().setPublishType(5);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderNegativeCount() {
+        new PublishConfig.Builder().setPublishCount(-4);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderNegativeTtl() {
+        new PublishConfig.Builder().setTtlSec(-10);
+    }
+
+    /*
+     * Ranging tests
+     */
+
+    /**
+     * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
+     * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
+     * listener removed).
+     */
+    @Test
+    public void testRangingCallbacks() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final int rangingId = 3482;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final RttManager.RttParams rttParams = new RttManager.RttParams();
+        rttParams.deviceType = RttManager.RTT_PEER_NAN;
+        rttParams.bssid = Integer.toString(1234);
+        final RttManager.RttResult rttResults = new RttManager.RttResult();
+        rttResults.distance = 10;
+
+        when(mockAwareService.startRanging(anyInt(), anyInt(),
+                any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
+                .forClass(RttManager.ParcelableRttParams.class);
+        ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
+                .forClass(RttManager.RttResult[].class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish successfully
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) start ranging
+        publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
+                mockRttListener);
+        inOrder.verify(mockAwareService).startRanging(eq(clientId), eq(sessionId),
+                rttParamCaptor.capture());
+        collector.checkThat("RttParams.deviceType", rttParams.deviceType,
+                equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
+        collector.checkThat("RttParams.bssid", rttParams.bssid,
+                equalTo(rttParamCaptor.getValue().mParams[0].bssid));
+
+        // (4) ranging success callback
+        clientProxyCallback.getValue().onRangingSuccess(rangingId,
+                new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
+        collector.checkThat("RttResult.distance", rttResults.distance,
+                equalTo(rttResultsCaptor.getValue()[0].distance));
+
+        // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
+        clientProxyCallback.getValue().onRangingAborted(rangingId);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+
+    /*
+     * Data-path tests
+     */
+
+    /**
+     * Validate that correct network specifier is generated for client-based data-path.
+     */
+    @Test
+    public void testNetworkSpecifierWithClient() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(123412);
+        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+        final String token = "Some arbitrary token string - can really be anything";
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish successfully
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) request a network specifier from the session
+        String networkSpecifier = publishSession.getValue().createNetworkSpecifier(peerHandle,
+                token.getBytes());
+
+        // validate format
+        JSONObject jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("session_id", sessionId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+        collector.checkThat("peer_id", peerHandle.peerId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+        collector.checkThat("token", tokenB64,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+
+    /**
+     * Validate that correct network specifier is generated for a direct data-path (i.e.
+     * specifying MAC address as opposed to a client-based oqaque specification).
+     */
+    @Test
+    public void testNetworkSpecifierDirect() throws Exception {
+        final int clientId = 134;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+        final String token = "Some arbitrary token string - can really be anything";
+
+        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        /* (2) request a direct network specifier*/
+        String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes());
+
+        /* validate format*/
+        JSONObject jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+                false)));
+        collector.checkThat("token", tokenB64,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+}