DO NOT MERGE Fix flaky IpPrefixTest.
am: f6b180ff1a  -s ours

Change-Id: I929a0ef7a6a044d2263a04867c2701653c3634de
diff --git a/Android.mk b/Android.mk
index bac3802..93c545e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,7 +45,6 @@
        core/java/android/app/admin/SecurityLogTags.logtags \
        core/java/android/content/EventLogTags.logtags \
        core/java/android/speech/tts/EventLogTags.logtags \
-       core/java/android/net/EventLogTags.logtags \
        core/java/android/webkit/EventLogTags.logtags \
        core/java/com/android/internal/logging/EventLogTags.logtags \
 
@@ -125,6 +124,8 @@
 	core/java/android/bluetooth/IBluetoothSap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
+	core/java/android/bluetooth/IBluetoothInputHost.aidl \
+	core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
 	core/java/android/bluetooth/IBluetoothGatt.aidl \
 	core/java/android/bluetooth/IBluetoothGattCallback.aidl \
 	core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 20cc231..f7a8b6c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6447,7 +6447,6 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
-    method public int getMetered();
     method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
@@ -6457,9 +6456,6 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
-    field public static final int METERED_ALL = -1; // 0xffffffff
-    field public static final int METERED_NO = 1; // 0x1
-    field public static final int METERED_YES = 2; // 0x2
     field public static final int ROAMING_ALL = -1; // 0xffffffff
     field public static final int ROAMING_NO = 1; // 0x1
     field public static final int ROAMING_YES = 2; // 0x2
@@ -36932,6 +36928,7 @@
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+    field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -60757,31 +60754,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/system-current.txt b/api/system-current.txt
index f25840d..258299e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40,6 +40,7 @@
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
+    field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
@@ -62,7 +63,7 @@
     field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
     field public static final java.lang.String BODY_SENSORS = "android.permission.BODY_SENSORS";
     field public static final java.lang.String BRICK = "android.permission.BRICK";
-    field public static final java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
+    field public static final deprecated java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
     field public static final java.lang.String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
     field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
     field public static final java.lang.String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
@@ -196,6 +197,7 @@
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
+    field public static final java.lang.String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
@@ -6739,7 +6741,6 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
-    method public int getMetered();
     method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
@@ -6749,9 +6750,6 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
-    field public static final int METERED_ALL = -1; // 0xffffffff
-    field public static final int METERED_NO = 1; // 0x1
-    field public static final int METERED_YES = 2; // 0x2
     field public static final int ROAMING_ALL = -1; // 0xffffffff
     field public static final int ROAMING_NO = 1; // 0x1
     field public static final int ROAMING_YES = 2; // 0x2
@@ -25760,9 +25758,17 @@
     field public static final android.os.Parcelable.Creator<android.net.RecommendationRequest> CREATOR;
   }
 
+  public static final class RecommendationRequest.Builder {
+    ctor public RecommendationRequest.Builder();
+    method public android.net.RecommendationRequest build();
+    method public android.net.RecommendationRequest.Builder setCurrentRecommendedWifiConfig(android.net.wifi.WifiConfiguration);
+    method public android.net.RecommendationRequest.Builder setNetworkCapabilities(android.net.NetworkCapabilities);
+    method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
+  }
+
   public final class RecommendationResult implements android.os.Parcelable {
-    method public static android.net.RecommendationResult createDoNotConnectRecommendation();
     method public static android.net.RecommendationResult createConnectRecommendation(android.net.wifi.WifiConfiguration);
+    method public static android.net.RecommendationResult createDoNotConnectRecommendation();
     method public int describeContents();
     method public android.net.wifi.WifiConfiguration getWifiConfiguration();
     method public boolean hasRecommendation();
@@ -25824,10 +25830,16 @@
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
+    method public int calculateBadge(int);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
     field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
     field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
+    field public static final int BADGING_4K = 30; // 0x1e
+    field public static final int BADGING_HD = 20; // 0x14
+    field public static final int BADGING_NONE = 0; // 0x0
+    field public static final int BADGING_SD = 10; // 0xa
     field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
     field public final android.os.Bundle attributes;
     field public final boolean meteredHint;
@@ -25835,6 +25847,9 @@
     field public final android.net.RssiCurve rssiCurve;
   }
 
+  public static abstract class ScoredNetwork.Badging implements java.lang.annotation.Annotation {
+  }
+
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
@@ -26843,6 +26858,8 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public boolean hasNoInternetAccess();
+    method public boolean isNoInternetAccessExpected();
     method public boolean isPasspoint();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
@@ -40065,6 +40082,7 @@
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+    field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -64328,31 +64346,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/test-current.txt b/api/test-current.txt
index 0349939..5819cf2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6456,7 +6456,6 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
-    method public int getMetered();
     method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
@@ -6466,9 +6465,6 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
-    field public static final int METERED_ALL = -1; // 0xffffffff
-    field public static final int METERED_NO = 1; // 0x1
-    field public static final int METERED_YES = 2; // 0x2
     field public static final int ROAMING_ALL = -1; // 0xffffffff
     field public static final int ROAMING_NO = 1; // 0x1
     field public static final int ROAMING_YES = 2; // 0x2
@@ -37014,6 +37010,7 @@
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+    field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -60848,31 +60845,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/compiled-classes-phone b/compiled-classes-phone
index 221d687..ec3371e 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -632,6 +632,7 @@
 android.bluetooth.BluetoothAudioConfig
 android.bluetooth.BluetoothClass
 android.bluetooth.BluetoothClass$1
+android.bluetooth.BluetoothCodecConfig
 android.bluetooth.BluetoothDevice
 android.bluetooth.BluetoothDevice$1
 android.bluetooth.BluetoothDevice$2
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8f42467..857a361 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -509,10 +509,23 @@
      * Common-path handling of app data dir creation
      */
     private static File ensurePrivateDirExists(File file) {
+        return ensurePrivateDirExists(file, 0771, -1);
+    }
+
+    private static File ensurePrivateCacheDirExists(File file) {
+        final int gid = UserHandle.getCacheAppGid(Process.myUid());
+        return ensurePrivateDirExists(file, 02771, gid);
+    }
+
+    private static File ensurePrivateDirExists(File file, int mode, int gid) {
         if (!file.exists()) {
+            final String path = file.getAbsolutePath();
             try {
-                Os.mkdir(file.getAbsolutePath(), 0771);
-                Os.chmod(file.getAbsolutePath(), 0771);
+                Os.mkdir(path, mode);
+                Os.chmod(path, mode);
+                if (gid != -1) {
+                    Os.chown(path, -1, gid);
+                }
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.EEXIST) {
                     // We must have raced with someone; that's okay
@@ -581,7 +594,7 @@
             if (mCacheDir == null) {
                 mCacheDir = new File(getDataDir(), "cache");
             }
-            return ensurePrivateDirExists(mCacheDir);
+            return ensurePrivateCacheDirExists(mCacheDir);
         }
     }
 
@@ -591,7 +604,7 @@
             if (mCodeCacheDir == null) {
                 mCodeCacheDir = new File(getDataDir(), "code_cache");
             }
-            return ensurePrivateDirExists(mCodeCacheDir);
+            return ensurePrivateCacheDirExists(mCodeCacheDir);
         }
     }
 
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 3670b91..226aa8f 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -164,29 +164,6 @@
         public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
 
         /** @hide */
-        @IntDef({METERED_ALL, METERED_NO, METERED_YES})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Metered {}
-
-        /**
-         * Combined usage across all metered states. Covers metered and unmetered usage.
-         */
-        public static final int METERED_ALL = -1;
-
-        /**
-         * Usage that occurs on an unmetered network.
-         */
-        public static final int METERED_NO = 0x1;
-
-        /**
-         * Usage that occurs on a metered network.
-         *
-         * <p>A network is classified as metered when the user is sensitive to heavy data usage on
-         * that connection.
-         */
-        public static final int METERED_YES = 0x2;
-
-        /** @hide */
         @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
         @Retention(RetentionPolicy.SOURCE)
         public @interface Roaming {}
@@ -223,7 +200,6 @@
         private int mUid;
         private int mTag;
         private int mState;
-        private int mMetered;
         private int mRoaming;
         private long mBeginTimeStamp;
         private long mEndTimeStamp;
@@ -256,15 +232,6 @@
             return tag;
         }
 
-        private static @Metered int convertMetered(int metered) {
-            switch (metered) {
-                case android.net.NetworkStats.METERED_ALL : return METERED_ALL;
-                case android.net.NetworkStats.METERED_NO: return METERED_NO;
-                case android.net.NetworkStats.METERED_YES: return METERED_YES;
-            }
-            return 0;
-        }
-
         private static @Roaming int convertRoaming(int roaming) {
             switch (roaming) {
                 case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
@@ -312,21 +279,6 @@
         }
 
         /**
-         * Metered state. One of the following values:<p/>
-         * <ul>
-         * <li>{@link #METERED_ALL}</li>
-         * <li>{@link #METERED_NO}</li>
-         * <li>{@link #METERED_YES}</li>
-         * </ul>
-         * <p>A network is classified as metered when the user is sensitive to heavy data usage on
-         * that connection. Apps may warn before using these networks for large downloads. The
-         * metered state can be set by the user within data usage network restrictions.
-         */
-        public @Metered int getMetered() {
-            return mMetered;
-        }
-
-        /**
          * Roaming state. One of the following values:<p/>
          * <ul>
          * <li>{@link #ROAMING_ALL}</li>
@@ -539,7 +491,6 @@
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
-        bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
         bucketOut.mBeginTimeStamp = mStartTimeStamp;
         bucketOut.mEndTimeStamp = mEndTimeStamp;
@@ -588,7 +539,6 @@
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
                 bucketOut.mState = Bucket.STATE_ALL;
-                bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 840413a..7961a72 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -51,17 +51,16 @@
  * {@link #querySummaryForUser} <p />
  * {@link #querySummary} <p />
  * These queries aggregate network usage across the whole interval. Therefore there will be only one
- * bucket for a particular key, state, metered and roaming combination. In case of the user-wide
- * and device-wide summaries a single bucket containing the totalised network usage is returned.
+ * bucket for a particular key and state and roaming combination. In case of the user-wide and
+ * device-wide summaries a single bucket containing the totalised network usage is returned.
  * <h3>
  * History queries
  * </h3>
  * {@link #queryDetailsForUid} <p />
  * {@link #queryDetails} <p />
- * These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
+ * These queries do not aggregate over time but do aggregate over state and roaming. Therefore there
+ * can be multiple buckets for a particular key but all Bucket's state is going to be
+ * {@link NetworkStats.Bucket#STATE_ALL} and all Bucket's roaming is going to be
  * {@link NetworkStats.Bucket#ROAMING_ALL}.
  * <p />
  * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
@@ -104,11 +103,10 @@
 
     /**
      * Query network usage statistics summaries. Result is summarised data usage for the whole
-     * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
-     * roaming. This means the bucket's start and end timestamp are going to be the same as the
-     * 'startTime' and 'endTime' parameters. State is going to be
-     * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+     * device. Result is a single Bucket aggregated over time, state, uid, tag and roaming. This
+     * means the bucket's start and end timestamp are going to be the same as the 'startTime' and
+     * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid
+     * {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE}
      * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -144,10 +142,8 @@
      * Query network usage statistics summaries. Result is summarised data usage for all uids
      * belonging to calling user. Result is a single Bucket aggregated over time, state and uid.
      * This means the bucket's start and end timestamp are going to be the same as the 'startTime'
-     * and 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL},
-     * uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
-     * metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * and 'endTime' parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid
+     * {@link NetworkStats.Bucket#UID_ALL}.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -181,10 +177,9 @@
     /**
      * Query network usage statistics summaries. Result filtered to include only uids belonging to
      * calling user. Result is aggregated over time, hence all buckets will have the same start and
-     * end timestamps. State is going to be {@link NetworkStats.Bucket#STATE_ALL},
-     * uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
-     * metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * end timestamps. Not aggregated over state or uid. This means buckets' start and end
+     * timestamps are going to be the same as the 'startTime' and 'endTime' parameters.
+     * State and uid are going to vary, and tag is going to be the same.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -267,12 +262,10 @@
 
     /**
      * Query network usage statistics details. Result filtered to include only uids belonging to
-     * calling user. Result is aggregated over state but not aggregated over time, uid, tag,
-     * metered, nor roaming. This means buckets' start and end timestamps are going to be between
-     * 'startTime' and 'endTime' parameters. State is going to be
-     * {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
-     * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
+     * calling user. Result is aggregated over state but not aggregated over time or uid. This means
+     * buckets' start and end timestamps are going to be between 'startTime' and 'endTime'
+     * parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
+     * tag {@link NetworkStats.Bucket#TAG_NONE} and roaming is going to be
      * {@link NetworkStats.Bucket#ROAMING_ALL}.
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 353c640..1165fce 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -102,6 +102,27 @@
         "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
 
     /**
+     * Intent used to broadcast the change in the Audio Codec state of the
+     * A2DP Source profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
+     *   connected, otherwise it is not included.</li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CODEC_CONFIG_CHANGED =
+        "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
+
+    /**
      * A2DP sink device is streaming music. This state can be one of
      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
      * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
@@ -543,6 +564,54 @@
     }
 
     /**
+     * Gets the current codec configuration.
+     *
+     * @return the current codec configuration
+     * @hide
+     */
+    public BluetoothCodecConfig getCodecConfig() {
+        if (DBG) Log.d(TAG, "getCodecConfig");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getCodecConfig();
+            }
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in getCodecConfig()", e);
+            return null;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Sets the codec configuration preference.
+     *
+     * @param codecConfig the codec configuration preference
+     * @hide
+     */
+    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
+        if (DBG) Log.d(TAG, "setCodecConfigPreference");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                mService.setCodecConfigPreference(codecConfig);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
+            return;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
      * Helper for converting a state to a string.
      *
      * For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4a97b07..f9be3a1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -680,30 +680,7 @@
     }
 
     /**
-     * Performs action based on user action to turn BT ON
-     * or OFF if BT is in BLE_ON state
-     */
-    private void notifyUserAction(boolean enable) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService == null) {
-                Log.e(TAG, "mService is null");
-                return;
-            }
-            if (enable) {
-                mService.onLeServiceUp(); //NA:TODO implementation pending
-            } else {
-                mService.onBrEdrDown(); //NA:TODO implementation pending
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-    }
-
-    /**
-     * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
+     * Turns off Bluetooth LE which was earlier turned on by calling enableBLE().
      *
      * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
      * to STATE_OFF and completely shut-down Bluetooth
@@ -733,61 +710,50 @@
         if (!isBleScanAlwaysAvailable()) return false;
 
         int state = getLeState();
-        if (state == BluetoothAdapter.STATE_ON) {
-            if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable");
+        if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
+            String packageName = ActivityThread.currentPackageName();
+            if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName);
             try {
-                mManagerService.updateBleAppCount(mToken, false);
+                mManagerService.updateBleAppCount(mToken, false, packageName);
             } catch (RemoteException e) {
                 Log.e(TAG, "", e);
             }
             return true;
-
-        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
-            if (DBG) Log.d (TAG, "STATE_BLE_ON");
-            int bleAppCnt = 0;
-            try {
-                bleAppCnt = mManagerService.updateBleAppCount(mToken, false);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-            if (bleAppCnt == 0) {
-                // Disable only if there are no other clients
-                notifyUserAction(false);
-            }
-            return true;
         }
 
-        if (DBG) Log.d (TAG, "STATE_OFF: Already disabled");
+        if (DBG) Log.d (TAG, "disableBLE(): Already disabled");
         return false;
     }
 
     /**
-     * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would
-     * EnableBLE, EnableBLE brings-up Bluetooth so that application can access
-     * only LE related feature (Bluetooth GATT layers interfaces using the respective class)
-     * EnableBLE in turn registers the existance of a special App which wants to
-     * turn on Bluetooth Low enrgy part without making it visible at the settings UI
-     * as Bluetooth ON.
-     * <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers
-     * the existance of special Application and doesn't do anything to current BT state.
-     * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth
-     * would stay in BLE_ON state so that LE features are still acessible to the special
-     * Applications.
+     * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE.
      *
-     * <p>This is an asynchronous call: it will return immediately, and
+     * enableBLE registers the existence of an app using only LE functions.
+     *
+     * enableBLE may enable Bluetooth to an LE only mode so that an app can use
+     * LE related features (BluetoothGatt or BluetoothGattServer classes)
+     *
+     * If the user disables Bluetooth while an app is registered to use LE only features,
+     * Bluetooth will remain on in LE only mode for the app.
+     *
+     * When Bluetooth is in LE only mode, it is not shown as ON to the UI.
+     *
+     * <p>This is an asynchronous call: it returns immediately, and
      * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes. If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time
-     * later transition to either {@link #STATE_OFF} or {@link
-     * #STATE_BLE_ON}. If this call returns false then there was an
-     * immediate problem that will prevent the adapter from being turned on -
-     * such as Airplane mode, or the adapter is already turned on.
-     * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various
+     * to be notified of adapter state changes.
+     *
+     * If this call returns * true, then the adapter state is either in a mode where
+     * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON},
+     * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}.
+     *
+     * If this call returns false then there was an immediate problem that prevents the
+     * adapter from being turned on - such as Airplane mode.
+     *
+     * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various
      * states, It includes all the classic Bluetooth Adapter states along with
      * internal BLE only states
      *
-     * @return true to indicate Bluetooth LE start-up has begun, or false on
+     * @return true to indicate Bluetooth LE will be available, or false on
      *         immediate error
      * @hide
      */
@@ -796,13 +762,14 @@
         if (!isBleScanAlwaysAvailable()) return false;
 
         try {
-            mManagerService.updateBleAppCount(mToken, true);
+            String packageName = ActivityThread.currentPackageName();
+            mManagerService.updateBleAppCount(mToken, true, packageName);
             if (isLeEnabled()) {
                 if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
                 return true;
             }
             if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
-            return mManagerService.enable(ActivityThread.currentPackageName());
+            return mManagerService.enable(packageName);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
@@ -1940,6 +1907,9 @@
         } else if (profile == BluetoothProfile.MAP_CLIENT) {
             BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.INPUT_HOST) {
+            BluetoothInputHost iHost = new BluetoothInputHost(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -2016,6 +1986,10 @@
                 BluetoothMapClient mapClient = (BluetoothMapClient)proxy;
                 mapClient.close();
                 break;
+            case BluetoothProfile.INPUT_HOST:
+                BluetoothInputHost iHost = (BluetoothInputHost) proxy;
+                iHost.close();
+                break;
         }
     }
 
@@ -2087,7 +2061,7 @@
             return true;
         }
         try {
-            return mManagerService.enableNoAutoConnect();
+            return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName());
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
similarity index 89%
copy from wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
copy to core/java/android/bluetooth/BluetoothCodecConfig.aidl
index a35e71d..553e66e 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.aware;
+package android.bluetooth;
 
-parcelable WifiAwareCharacteristics;
+parcelable BluetoothCodecConfig;
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
new file mode 100644
index 0000000..52cd2de
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -0,0 +1,294 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the codec configuration for a Bluetooth A2DP source device.
+ *
+ * {@see BluetoothA2dp}
+ *
+ * {@hide}
+ */
+public final class BluetoothCodecConfig implements Parcelable {
+
+    /**
+     * Extra for the codec configuration intents of the individual profiles.
+     *
+     * This extra represents the current codec configuration of the A2DP
+     * profile.
+     */
+    public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG";
+
+    /**
+     * Extra for the codec configuration intents of the individual profiles.
+     *
+     * This extra represents the previous codec configuration of the A2DP
+     * profile.
+     */
+    public static final String EXTRA_PREVIOUS_CODEC_CONFIG =
+        "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG";
+
+    // Add an entry for each source codec here.
+    // NOTE: The values should be same as those listed in the following file:
+    //   hardware/libhardware/include/hardware/bt_av.h
+    public static final int SOURCE_CODEC_TYPE_SBC     = 0;
+    public static final int SOURCE_CODEC_TYPE_APTX    = 1;
+    public static final int SOURCE_CODEC_TYPE_APTX_HD = 2;
+    public static final int SOURCE_CODEC_TYPE_LDAC    = 3;
+
+    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
+
+    public static final int CODEC_PRIORITY_DEFAULT = 0;
+    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
+
+    public static final int SAMPLE_RATE_NONE   = 0;
+    public static final int SAMPLE_RATE_44100  = 0x1 << 0;
+    public static final int SAMPLE_RATE_48000  = 0x1 << 1;
+    public static final int SAMPLE_RATE_88200  = 0x1 << 2;
+    public static final int SAMPLE_RATE_96000  = 0x1 << 3;
+    public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+    public static final int SAMPLE_RATE_192000 = 0x1 << 5;
+
+    public static final int BITS_PER_SAMPLE_NONE = 0;
+    public static final int BITS_PER_SAMPLE_16   = 0x1 << 0;
+    public static final int BITS_PER_SAMPLE_24   = 0x1 << 1;
+    public static final int BITS_PER_SAMPLE_32   = 0x1 << 2;
+
+    public static final int CHANNEL_MODE_NONE   = 0;
+    public static final int CHANNEL_MODE_MONO   = 0x1 << 0;
+    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
+
+    private final int mCodecType;
+    private final int mCodecPriority;
+    private final int mSampleRate;
+    private final int mBitsPerSample;
+    private final int mChannelMode;
+    private final long mCodecSpecific1;
+    private final long mCodecSpecific2;
+    private final long mCodecSpecific3;
+    private final long mCodecSpecific4;
+
+    public BluetoothCodecConfig(int codecType, int codecPriority,
+                                int sampleRate, int bitsPerSample,
+                                int channelMode,long codecSpecific1,
+                                long codecSpecific2, long codecSpecific3,
+                                long codecSpecific4) {
+        mCodecType = codecType;
+        mCodecPriority = codecPriority;
+        mSampleRate = sampleRate;
+        mBitsPerSample = bitsPerSample;
+        mChannelMode = channelMode;
+        mCodecSpecific1 = codecSpecific1;
+        mCodecSpecific2 = codecSpecific2;
+        mCodecSpecific3 = codecSpecific3;
+        mCodecSpecific4 = codecSpecific4;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothCodecConfig) {
+            BluetoothCodecConfig other = (BluetoothCodecConfig)o;
+            return (other.mCodecType == mCodecType &&
+                    other.mCodecPriority == mCodecPriority &&
+                    other.mSampleRate == mSampleRate &&
+                    other.mBitsPerSample == mBitsPerSample &&
+                    other.mChannelMode == mChannelMode &&
+                    other.mCodecSpecific1 == mCodecSpecific1 &&
+                    other.mCodecSpecific2 == mCodecSpecific2 &&
+                    other.mCodecSpecific3 == mCodecSpecific3 &&
+                    other.mCodecSpecific4 == mCodecSpecific4);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
+                            mBitsPerSample, mChannelMode, mCodecSpecific1,
+                            mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
+    }
+
+    @Override
+    public String toString() {
+        return "{mCodecType:" + mCodecType +
+            ",mCodecPriority:" + mCodecPriority +
+            ",mSampleRate:" + String.format("0x%x", mSampleRate) +
+            ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) +
+            ",mChannelMode:" + String.format("0x%x", mChannelMode) +
+            ",mCodecSpecific1:" + mCodecSpecific1 +
+            ",mCodecSpecific2:" + mCodecSpecific2 +
+            ",mCodecSpecific3:" + mCodecSpecific3 +
+            ",mCodecSpecific4:" + mCodecSpecific4 + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothCodecConfig> CREATOR =
+            new Parcelable.Creator<BluetoothCodecConfig>() {
+        public BluetoothCodecConfig createFromParcel(Parcel in) {
+            final int codecType = in.readInt();
+            final int codecPriority = in.readInt();
+            final int sampleRate = in.readInt();
+            final int bitsPerSample = in.readInt();
+            final int channelMode = in.readInt();
+            final long codecSpecific1 = in.readLong();
+            final long codecSpecific2 = in.readLong();
+            final long codecSpecific3 = in.readLong();
+            final long codecSpecific4 = in.readLong();
+            return new BluetoothCodecConfig(codecType, codecPriority,
+                                            sampleRate, bitsPerSample,
+                                            channelMode, codecSpecific1,
+                                            codecSpecific2, codecSpecific3,
+                                            codecSpecific4);
+        }
+        public BluetoothCodecConfig[] newArray(int size) {
+            return new BluetoothCodecConfig[size];
+        }
+    };
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCodecType);
+        out.writeInt(mCodecPriority);
+        out.writeInt(mSampleRate);
+        out.writeInt(mBitsPerSample);
+        out.writeInt(mChannelMode);
+        out.writeLong(mCodecSpecific1);
+        out.writeLong(mCodecSpecific2);
+        out.writeLong(mCodecSpecific3);
+        out.writeLong(mCodecSpecific4);
+    }
+
+    /**
+     * Returns the codec type.
+     * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
+     *
+     * @return the codec type
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    /**
+     * Returns the codec selection priority.
+     * The codec selection priority is relative to other codecs: larger value
+     * means higher priority. If 0, reset to default.
+     *
+     * @return the codec priority
+     */
+    public int getCodecPriority() {
+        return mCodecPriority;
+    }
+
+    /**
+     * Returns the codec sample rate. The value can be a bitmask with all
+     * supported sample rates:
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000}
+     *
+     * @return the codec sample rate
+     */
+    public int getSampleRate() {
+        return mSampleRate;
+    }
+
+    /**
+     * Returns the codec bits per sample. The value can be a bitmask with all
+     * bits per sample supported:
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32}
+     *
+     * @return the codec bits per sample
+     */
+    public int getBitsPerSample() {
+        return mBitsPerSample;
+    }
+
+    /**
+     * Returns the codec channel mode. The value can be a bitmask with all
+     * supported channel modes:
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO}
+     *
+     * @return the codec channel mode
+     */
+    public int getChannelMode() {
+        return mChannelMode;
+    }
+
+    /**
+     * Returns a codec specific value1.
+     *
+     * @return a codec specific value1.
+     */
+    public long getCodecSpecific1() {
+        return mCodecSpecific1;
+    }
+
+    /**
+     * Returns a codec specific value2.
+     *
+     * @return a codec specific value2
+     */
+    public long getCodecSpecific2() {
+        return mCodecSpecific2;
+    }
+
+    /**
+     * Returns a codec specific value3.
+     *
+     * @return a codec specific value3
+     */
+    public long getCodecSpecific3() {
+        return mCodecSpecific3;
+    }
+
+    /**
+     * Returns a codec specific value4.
+     *
+     * @return a codec specific value4
+     */
+    public long getCodecSpecific4() {
+        return mCodecSpecific4;
+    }
+
+    /**
+     * Checks whether the audio feeding parameters are same.
+     *
+     * @param other the codec config to compare against
+     * @return true if the audio feeding parameters are same, otherwise false
+     */
+    public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
+        return (other != null && other.mSampleRate == mSampleRate &&
+                other.mBitsPerSample == mBitsPerSample &&
+                other.mChannelMode == mChannelMode);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl
new file mode 100644
index 0000000..283a717
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 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.bluetooth;
+
+parcelable BluetoothHidDeviceAppConfiguration;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
new file mode 100644
index 0000000..05ba64e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
@@ -0,0 +1,70 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppConfiguration implements Parcelable {
+    private final long mHash;
+
+    BluetoothHidDeviceAppConfiguration() {
+        Random rnd = new Random();
+        mHash = rnd.nextLong();
+    }
+
+    BluetoothHidDeviceAppConfiguration(long hash) {
+        mHash = hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothHidDeviceAppConfiguration) {
+            BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o;
+            return mHash == config.mHash;
+        }
+        return false;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothHidDeviceAppConfiguration> CREATOR =
+        new Parcelable.Creator<BluetoothHidDeviceAppConfiguration>() {
+
+        @Override
+        public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) {
+            long hash = in.readLong();
+            return new BluetoothHidDeviceAppConfiguration(hash);
+        }
+
+        @Override
+        public BluetoothHidDeviceAppConfiguration[] newArray(int size) {
+            return new BluetoothHidDeviceAppConfiguration[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(mHash);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
new file mode 100644
index 0000000..14f9114
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 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.bluetooth;
+
+parcelable BluetoothHidDeviceAppQosSettings;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
new file mode 100644
index 0000000..0d6530c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -0,0 +1,97 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
+
+    final public int serviceType;
+    final public int tokenRate;
+    final public int tokenBucketSize;
+    final public int peakBandwidth;
+    final public int latency;
+    final public int delayVariation;
+
+    final static public int SERVICE_NO_TRAFFIC = 0x00;
+    final static public int SERVICE_BEST_EFFORT = 0x01;
+    final static public int SERVICE_GUARANTEED = 0x02;
+
+    final static public int MAX = (int) 0xffffffff;
+
+    public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
+            int peakBandwidth,
+            int latency, int delayVariation) {
+        this.serviceType = serviceType;
+        this.tokenRate = tokenRate;
+        this.tokenBucketSize = tokenBucketSize;
+        this.peakBandwidth = peakBandwidth;
+        this.latency = latency;
+        this.delayVariation = delayVariation;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothHidDeviceAppQosSettings) {
+            BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
+            return false;
+        }
+        return false;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR =
+        new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() {
+
+        @Override
+        public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) {
+
+            return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(),
+                    in.readInt(),
+                    in.readInt(), in.readInt());
+        }
+
+        @Override
+        public BluetoothHidDeviceAppQosSettings[] newArray(int size) {
+            return new BluetoothHidDeviceAppQosSettings[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(serviceType);
+        out.writeInt(tokenRate);
+        out.writeInt(tokenBucketSize);
+        out.writeInt(peakBandwidth);
+        out.writeInt(latency);
+        out.writeInt(delayVariation);
+    }
+
+    public int[] toArray() {
+        return new int[] {
+                serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
+        };
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
new file mode 100644
index 0000000..87dd10e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 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.bluetooth;
+
+parcelable BluetoothHidDeviceAppSdpSettings;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
new file mode 100644
index 0000000..f9a2245
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
+
+    final public String name;
+    final public String description;
+    final public String provider;
+    final public byte subclass;
+    final public byte[] descriptors;
+
+    public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider,
+            byte subclass, byte[] descriptors) {
+        this.name = name;
+        this.description = description;
+        this.provider = provider;
+        this.subclass = subclass;
+        this.descriptors = descriptors.clone();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothHidDeviceAppSdpSettings) {
+            BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
+            return false;
+        }
+        return false;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR =
+        new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() {
+
+        @Override
+        public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) {
+
+            return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(),
+                    in.readString(), in.readByte(), in.createByteArray());
+        }
+
+        @Override
+        public BluetoothHidDeviceAppSdpSettings[] newArray(int size) {
+            return new BluetoothHidDeviceAppSdpSettings[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(name);
+        out.writeString(description);
+        out.writeString(provider);
+        out.writeByte(subclass);
+        out.writeByteArray(descriptors);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
new file mode 100644
index 0000000..f519776
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -0,0 +1,128 @@
+/*
+ * 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.bluetooth;
+
+import android.util.Log;
+
+/** @hide */
+public abstract class BluetoothHidDeviceCallback {
+
+    private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName();
+
+    /**
+     * Callback called when application registration state changes. Usually it's
+     * called due to either
+     * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[],
+     * BluetoothHidDeviceCallback)}
+     * or
+     * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
+     * , but can be also unsolicited in case e.g. Bluetooth was turned off in
+     * which case application is unregistered automatically.
+     *
+     * @param pluggedDevice {@link BluetoothDevice} object which represents host
+     *            that currently has Virtual Cable established with device. Only
+     *            valid when application is registered, can be <code>null</code>
+     *            .
+     * @param config {@link BluetoothHidDeviceAppConfiguration} object which
+     *            represents token required to unregister application using
+     *            {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
+     *            .
+     * @param registered <code>true</code> if application is registered,
+     *            <code>false</code> otherwise.
+     */
+    public void onAppStatusChanged(BluetoothDevice pluggedDevice,
+            BluetoothHidDeviceAppConfiguration config, boolean registered) {
+        Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
+                + registered);
+    }
+
+    /**
+     * Callback called when connection state with remote host was changed.
+     * Application can assume than Virtual Cable is established when called with
+     * {@link BluetoothProfile#STATE_CONNECTED} <code>state</code>.
+     *
+     * @param device {@link BluetoothDevice} object representing host device
+     *            which connection state was changed.
+     * @param state Connection state as defined in {@link BluetoothProfile}.
+     */
+    public void onConnectionStateChanged(BluetoothDevice device, int state) {
+        Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
+    }
+
+    /**
+     * Callback called when GET_REPORT is received from remote host. Should be
+     * replied by application using
+     * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}.
+     *
+     * @param type Requested Report Type.
+     * @param id Requested Report Id, can be 0 if no Report Id are defined in
+     *            descriptor.
+     * @param bufferSize Requested buffer size, application shall respond with
+     *            at least given number of bytes.
+     */
+    public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+        Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
+                + bufferSize);
+    }
+
+    /**
+     * Callback called when SET_REPORT is received from remote host. In case
+     * received data are invalid, application shall respond with
+     * {@link BluetoothHidDevice#reportError(BluetoothDevice)}.
+     *
+     * @param type Report Type.
+     * @param id Report Id.
+     * @param data Report data.
+     */
+    public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+        Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
+    }
+
+    /**
+     * Callback called when SET_PROTOCOL is received from remote host.
+     * Application shall use this information to send only reports valid for
+     * given protocol mode. By default,
+     * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
+     *
+     * @param protocol Protocol Mode.
+     */
+    public void onSetProtocol(BluetoothDevice device, byte protocol) {
+        Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
+    }
+
+    /**
+     * Callback called when report data is received over interrupt channel.
+     * Report Type is assumed to be
+     * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
+     *
+     * @param reportId Report Id.
+     * @param data Report data.
+     */
+    public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+        Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId);
+    }
+
+    /**
+     * Callback called when Virtual Cable is removed. This can be either due to
+     * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote
+     * side. After this callback is received connection will be disconnected
+     * automatically.
+     */
+    public void onVirtualCableUnplug(BluetoothDevice device) {
+        Log.d(TAG, "onVirtualCableUnplug: device=" + device);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
new file mode 100644
index 0000000..68d105f
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -0,0 +1,558 @@
+/*
+ * 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.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class BluetoothInputHost implements BluetoothProfile {
+
+    private static final String TAG = BluetoothInputHost.class.getSimpleName();
+
+    /**
+     * Intent used to broadcast the change in connection state of the Input
+     * Host profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Constants representing device subclass.
+     *
+     * @see #registerApp(String, String, String, byte, byte[],
+     *      BluetoothHidDeviceCallback)
+     */
+    public static final byte SUBCLASS1_NONE = (byte) 0x00;
+    public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
+    public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
+    public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
+
+    public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
+    public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
+    public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
+    public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
+    public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
+    public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05;
+    public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
+
+    /**
+     * Constants representing report types.
+     *
+     * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int)
+     * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])
+     * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[])
+     */
+    public static final byte REPORT_TYPE_INPUT = (byte) 1;
+    public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
+    public static final byte REPORT_TYPE_FEATURE = (byte) 3;
+
+    /**
+     * Constants representing error response for Set Report.
+     *
+     * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])
+     */
+    public static final byte ERROR_RSP_SUCCESS = (byte) 0;
+    public static final byte ERROR_RSP_NOT_READY = (byte) 1;
+    public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
+    public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
+    public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
+    public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
+
+    /**
+     * Constants representing protocol mode used set by host. Default is always
+     * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise.
+     *
+     * @see BluetoothHidDeviceCallback#onSetProtocol(byte)
+     */
+    public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
+    public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
+
+    private Context mContext;
+
+    private ServiceListener mServiceListener;
+
+    private IBluetoothInputHost mService;
+
+    private BluetoothAdapter mAdapter;
+
+    private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
+
+        private BluetoothHidDeviceCallback mCallback;
+
+        public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void onAppStatusChanged(BluetoothDevice pluggedDevice,
+                BluetoothHidDeviceAppConfiguration config, boolean registered) {
+            mCallback.onAppStatusChanged(pluggedDevice, config, registered);
+        }
+
+        @Override
+        public void onConnectionStateChanged(BluetoothDevice device, int state) {
+            mCallback.onConnectionStateChanged(device, state);
+        }
+
+        @Override
+        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+            mCallback.onGetReport(device, type, id, bufferSize);
+        }
+
+        @Override
+        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+            mCallback.onSetReport(device, type, id, data);
+        }
+
+        @Override
+        public void onSetProtocol(BluetoothDevice device, byte protocol) {
+            mCallback.onSetProtocol(device, protocol);
+        }
+
+        @Override
+        public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+            mCallback.onIntrData(device, reportId, data);
+        }
+
+        @Override
+        public void onVirtualCableUnplug(BluetoothDevice device) {
+            mCallback.onVirtualCableUnplug(device);
+        }
+    }
+
+    final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+        new IBluetoothStateChangeCallback.Stub() {
+
+        public void onBluetoothStateChange(boolean up) {
+            Log.d(TAG, "onBluetoothStateChange: up=" + up);
+            synchronized (mConnection) {
+                if (!up) {
+                    Log.d(TAG,"Unbinding service...");
+                    if (mService != null) {
+                        mService = null;
+                        try {
+                            mContext.unbindService(mConnection);
+                        } catch (IllegalArgumentException e) {
+                            Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e);
+                        }
+                    }
+                } else {
+                    try {
+                        if (mService == null) {
+                            Log.d(TAG,"Binding HID Device service...");
+                            doBind();
+                        }
+                    } catch (IllegalStateException e) {
+                        Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e);
+                    } catch (SecurityException e) {
+                        Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e);
+                    }
+                }
+            }
+        }
+    };
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.d(TAG, "onServiceConnected()");
+
+            mService = IBluetoothInputHost.Stub.asInterface(service);
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST,
+                    BluetoothInputHost.this);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Log.d(TAG, "onServiceDisconnected()");
+
+            mService = null;
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST);
+            }
+        }
+    };
+
+    BluetoothInputHost(Context context, ServiceListener listener) {
+        Log.v(TAG, "BluetoothInputHost");
+
+        mContext = context;
+        mServiceListener = listener;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothInputHost.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
+            return false;
+        }
+        Log.d(TAG, "Bound to HID Device Service");
+        return true;
+    }
+
+    void close() {
+        Log.v(TAG, "close()");
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+
+        synchronized (mConnection) {
+            if (mService != null) {
+                mService = null;
+                try {
+                    mContext.unbindService(mConnection);
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG,"close: could not unbind HID Dev service: ", e);
+                }
+           }
+        }
+
+        mServiceListener = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        Log.v(TAG, "getConnectedDevices()");
+
+        if (mService != null) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
+
+        if (mService != null) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        Log.v(TAG, "getConnectionState(): device=" + device);
+
+        if (mService != null) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return STATE_DISCONNECTED;
+    }
+
+    /**
+     * Registers application to be used for HID device. Connections to HID
+     * Device are only possible when application is registered. Only one
+     * application can be registered at time. When no longer used, application
+     * should be unregistered using
+     * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}.
+     *
+     * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of
+     *             HID Device SDP record.
+     * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of
+     *             Incoming QoS Settings.
+     * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of
+     *             Outgoing QoS Settings.
+     * @param callback {@link BluetoothHidDeviceCallback} object to which
+     *            callback messages will be sent.
+     * @return
+     */
+    public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
+            BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
+            BluetoothHidDeviceCallback callback) {
+        Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos
+                + " callback=" + callback);
+
+        boolean result = false;
+
+        if (sdp == null || callback == null) {
+            return false;
+        }
+
+        if (mService != null) {
+            try {
+                BluetoothHidDeviceAppConfiguration config =
+                    new BluetoothHidDeviceAppConfiguration();
+                BluetoothHidDeviceCallbackWrapper cbw =
+                    new BluetoothHidDeviceCallbackWrapper(callback);
+                result = mService.registerApp(config, sdp, inQos, outQos, cbw);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Unregisters application. Active connection will be disconnected and no
+     * new connections will be allowed until registered again using
+     * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)}
+     *
+     * @param config {@link BluetoothHidDeviceAppConfiguration} object as
+     *            obtained from
+     *            {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice,
+     *            BluetoothHidDeviceAppConfiguration, boolean)}
+     *
+     * @return
+     */
+    public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
+        Log.v(TAG, "unregisterApp()");
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.unregisterApp(config);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Sends report to remote host using interrupt channel.
+     *
+     * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id
+     *            are not defined in descriptor.
+     * @param data Report data, not including Report Id.
+     * @return
+     */
+    public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.sendReport(device, id, data);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Sends report to remote host as reply for GET_REPORT request from
+     * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
+     *
+     * @param type Report Type, as in request.
+     * @param id Report Id, as in request.
+     * @param data Report data, not including Report Id.
+     * @return
+     */
+    public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+        Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.replyReport(device, type, id, data);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Sends error handshake message as reply for invalid SET_REPORT request
+     * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
+     *
+     * @param error Error to be sent for SET_REPORT via HANDSHAKE.
+     * @return
+     */
+    public boolean reportError(BluetoothDevice device, byte error) {
+        Log.v(TAG, "reportError(): device=" + device + " error=" + error);
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.reportError(device, error);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Sends Virtual Cable Unplug to currently connected host.
+     *
+     * @return
+     */
+    public boolean unplug(BluetoothDevice device) {
+        Log.v(TAG, "unplug(): device=" + device);
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.unplug(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Initiates connection to host which currently has Virtual Cable
+     * established with device.
+     *
+     * @return
+     */
+    public boolean connect(BluetoothDevice device) {
+        Log.v(TAG, "connect(): device=" + device);
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+
+    /**
+     * Disconnects from currently connected host.
+     *
+     * @return
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        Log.v(TAG, "disconnect(): device=" + device);
+
+        boolean result = false;
+
+        if (mService != null) {
+            try {
+                result = mService.disconnect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return result;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index f363607..2f64c71 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -143,11 +143,17 @@
     public static final int MAP_CLIENT = 18;
 
     /**
+     * Input Host
+     * @hide
+     */
+    static public final int INPUT_HOST = 19;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      * @hide
      */
-    public static final int MAX_PROFILE_ID = 17;
+    public static final int MAX_PROFILE_ID = 19;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 26ff9e27..5b524eb 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothDevice;
 
 /**
@@ -36,4 +37,6 @@
     oneway void adjustAvrcpAbsoluteVolume(int direction);
     oneway void setAvrcpAbsoluteVolume(int volume);
     boolean isA2dpPlaying(in BluetoothDevice device);
+    BluetoothCodecConfig getCodecConfig();
+    oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
 }
diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
new file mode 100644
index 0000000..a737198
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDeviceAppConfiguration;
+
+/** @hide */
+interface IBluetoothHidDeviceCallback {
+   void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered);
+   void onConnectionStateChanged(in BluetoothDevice device, in int state);
+   void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize);
+   void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+   void onSetProtocol(in BluetoothDevice device, in byte protocol);
+   void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data);
+   void onVirtualCableUnplug(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/IBluetoothInputHost.aidl b/core/java/android/bluetooth/IBluetoothInputHost.aidl
new file mode 100644
index 0000000..6c4993f
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothInputHost.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDeviceAppConfiguration;
+import android.bluetooth.IBluetoothHidDeviceCallback;
+import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
+import android.bluetooth.BluetoothHidDeviceAppQosSettings;
+
+/** @hide */
+interface IBluetoothInputHost {
+    boolean registerApp(in BluetoothHidDeviceAppConfiguration config,
+            in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos,
+            in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback);
+    boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config);
+    boolean sendReport(in BluetoothDevice device, in int id, in byte[] data);
+    boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+    boolean reportError(in BluetoothDevice device, byte error);
+    boolean unplug(in BluetoothDevice device);
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 2ab9ae8..5afd774 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -35,8 +35,8 @@
     void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
     boolean isEnabled();
     boolean enable(String packageName);
-    boolean enableNoAutoConnect();
-    boolean disable( String packageName, boolean persist);
+    boolean enableNoAutoConnect(String packageName);
+    boolean disable(String packageName, boolean persist);
     int getState();
     IBluetoothGatt getBluetoothGatt();
 
@@ -47,6 +47,6 @@
     String getName();
 
     boolean isBleScanAlwaysAvailable();
-    int updateBleAppCount(IBinder b, boolean enable);
+    int updateBleAppCount(IBinder b, boolean enable, String packageName);
     boolean isBleAppPresent();
 }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index d4dcacc..0c4573b 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -391,7 +391,7 @@
     public ContextHubManager(Context context, Looper mainLooper) {
         mMainLooper = mainLooper;
 
-        IBinder b = ServiceManager.getService(ContextHubService.CONTEXTHUB_SERVICE);
+        IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE);
         if (b != null) {
             mContextHubService = IContextHubService.Stub.asInterface(b);
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 51431eb..0afb546 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2079,8 +2079,6 @@
     @SystemApi
     public void startTethering(int type, boolean showProvisioningUi,
             final OnStartTetheringCallback callback, Handler handler) {
-        checkNotNull(callback, "OnStartTetheringCallback cannot be null.");
-
         ResultReceiver wrappedCallback = new ResultReceiver(handler) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
@@ -2091,7 +2089,6 @@
                 }
             }
         };
-
         try {
             mService.startTethering(type, wrappedCallback, showProvisioningUi);
         } catch (RemoteException e) {
@@ -2595,8 +2592,7 @@
 
         /**
          * Called if no network is found in the given timeout time.  If no timeout is given,
-         * this will not be called. The associated {@link NetworkRequest} will have already
-         * been removed and released, as if {@link #unregisterNetworkCallback} had been called.
+         * this will not be called.
          * @hide
          */
         public void onUnavailable() {}
@@ -2660,7 +2656,6 @@
     public static final int CALLBACK_IP_CHANGED          = BASE + 7;
     /** @hide */
     public static final int CALLBACK_RELEASED            = BASE + 8;
-    // TODO: consider deleting CALLBACK_EXIT and shifting following enum codes down by 1.
     /** @hide */
     public static final int CALLBACK_EXIT                = BASE + 9;
     /** @hide obj = NetworkCapabilities, arg1 = seq number */
@@ -2670,38 +2665,25 @@
     /** @hide */
     public static final int CALLBACK_RESUMED             = BASE + 12;
 
-    /** @hide */
-    public static String getCallbackName(int whichCallback) {
-        switch (whichCallback) {
-            case CALLBACK_PRECHECK:     return "CALLBACK_PRECHECK";
-            case CALLBACK_AVAILABLE:    return "CALLBACK_AVAILABLE";
-            case CALLBACK_LOSING:       return "CALLBACK_LOSING";
-            case CALLBACK_LOST:         return "CALLBACK_LOST";
-            case CALLBACK_UNAVAIL:      return "CALLBACK_UNAVAIL";
-            case CALLBACK_CAP_CHANGED:  return "CALLBACK_CAP_CHANGED";
-            case CALLBACK_IP_CHANGED:   return "CALLBACK_IP_CHANGED";
-            case CALLBACK_RELEASED:     return "CALLBACK_RELEASED";
-            case CALLBACK_EXIT:         return "CALLBACK_EXIT";
-            case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
-            case CALLBACK_SUSPENDED:    return "CALLBACK_SUSPENDED";
-            case CALLBACK_RESUMED:      return "CALLBACK_RESUMED";
-            default:
-                return Integer.toString(whichCallback);
-        }
-    }
-
     private class CallbackHandler extends Handler {
+        private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap;
+        private final AtomicInteger mRefCount;
         private static final String TAG = "ConnectivityManager.CallbackHandler";
+        private final ConnectivityManager mCm;
         private static final boolean DBG = false;
 
-        CallbackHandler(Looper looper) {
+        CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap,
+                AtomicInteger refCount, ConnectivityManager cm) {
             super(looper);
+            mCallbackMap = callbackMap;
+            mRefCount = refCount;
+            mCm = cm;
         }
 
         @Override
         public void handleMessage(Message message) {
-            NetworkRequest request = getObject(message, NetworkRequest.class);
-            Network network = getObject(message, Network.class);
+            NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
+            Network network = (Network) getObject(message, Network.class);
             if (DBG) {
                 Log.d(TAG, whatToString(message.what) + " for network " + network);
             }
@@ -2744,7 +2726,9 @@
                 case CALLBACK_CAP_CHANGED: {
                     NetworkCallback callback = getCallback(request, "CAP_CHANGED");
                     if (callback != null) {
-                        NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+                        NetworkCapabilities cap = (NetworkCapabilities)getObject(message,
+                                NetworkCapabilities.class);
+
                         callback.onCapabilitiesChanged(network, cap);
                     }
                     break;
@@ -2752,7 +2736,9 @@
                 case CALLBACK_IP_CHANGED: {
                     NetworkCallback callback = getCallback(request, "IP_CHANGED");
                     if (callback != null) {
-                        LinkProperties lp = getObject(message, LinkProperties.class);
+                        LinkProperties lp = (LinkProperties)getObject(message,
+                                LinkProperties.class);
+
                         callback.onLinkPropertiesChanged(network, lp);
                     }
                     break;
@@ -2772,16 +2758,24 @@
                     break;
                 }
                 case CALLBACK_RELEASED: {
-                    final NetworkCallback callback;
-                    synchronized(sCallbacks) {
-                        callback = sCallbacks.remove(request);
+                    NetworkCallback callback = null;
+                    synchronized(mCallbackMap) {
+                        callback = mCallbackMap.remove(request);
                     }
-                    if (callback == null) {
+                    if (callback != null) {
+                        synchronized(mRefCount) {
+                            if (mRefCount.decrementAndGet() == 0) {
+                                getLooper().quit();
+                            }
+                        }
+                    } else {
                         Log.e(TAG, "callback not found for RELEASED message");
                     }
                     break;
                 }
                 case CALLBACK_EXIT: {
+                    Log.d(TAG, "Listener quitting");
+                    getLooper().quit();
                     break;
                 }
                 case EXPIRE_LEGACY_REQUEST: {
@@ -2791,14 +2785,14 @@
             }
         }
 
-        private <T> T getObject(Message msg, Class<T> c) {
-            return (T) msg.getData().getParcelable(c.getSimpleName());
+        private Object getObject(Message msg, Class c) {
+            return msg.getData().getParcelable(c.getSimpleName());
         }
 
         private NetworkCallback getCallback(NetworkRequest req, String name) {
             NetworkCallback callback;
-            synchronized(sCallbacks) {
-                callback = sCallbacks.get(req);
+            synchronized(mCallbackMap) {
+                callback = mCallbackMap.get(req);
             }
             if (callback == null) {
                 Log.e(TAG, "callback not found for " + name + " message");
@@ -2807,56 +2801,63 @@
         }
     }
 
-    private CallbackHandler getHandler() {
-        synchronized (sCallbacks) {
-            if (sCallbackHandler == null) {
-                sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper());
+    private void incCallbackHandlerRefCount() {
+        synchronized(sCallbackRefCount) {
+            if (sCallbackRefCount.incrementAndGet() == 1) {
+                // TODO: switch this to ConnectivityThread
+                HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
+                callbackThread.start();
+                sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
+                        sNetworkCallback, sCallbackRefCount, this);
             }
-            return sCallbackHandler;
         }
     }
 
-    static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
-    static CallbackHandler sCallbackHandler;
+    private void decCallbackHandlerRefCount() {
+        synchronized(sCallbackRefCount) {
+            if (sCallbackRefCount.decrementAndGet() == 0) {
+                sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
+                sCallbackHandler = null;
+            }
+        }
+    }
+
+    static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback =
+            new HashMap<NetworkRequest, NetworkCallback>();
+    static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
+    static CallbackHandler sCallbackHandler = null;
 
     private final static int LISTEN  = 1;
     private final static int REQUEST = 2;
 
     private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
-            NetworkCallback callback, int timeoutMs, int action, int legacyType) {
-        return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType);
-    }
-
-    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
-            NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) {
-        if (callback == null) {
+            NetworkCallback networkCallback, int timeoutSec, int action,
+            int legacyType) {
+        if (networkCallback == null) {
             throw new IllegalArgumentException("null NetworkCallback");
         }
         if (need == null && action != REQUEST) {
             throw new IllegalArgumentException("null NetworkCapabilities");
         }
-        // TODO: throw an exception if callback.networkRequest is not null.
-        // http://b/20701525
-        final NetworkRequest request;
         try {
-            synchronized(sCallbacks) {
-                Messenger messenger = new Messenger(handler);
-                Binder binder = new Binder();
+            incCallbackHandlerRefCount();
+            synchronized(sNetworkCallback) {
                 if (action == LISTEN) {
-                    request = mService.listenForNetwork(need, messenger, binder);
+                    networkCallback.networkRequest = mService.listenForNetwork(need,
+                            new Messenger(sCallbackHandler), new Binder());
                 } else {
-                    request = mService.requestNetwork(
-                            need, messenger, timeoutMs, binder, legacyType);
+                    networkCallback.networkRequest = mService.requestNetwork(need,
+                            new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
                 }
-                if (request != null) {
-                    sCallbacks.put(request, callback);
+                if (networkCallback.networkRequest != null) {
+                    sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
                 }
-                callback.networkRequest = request;
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        return request;
+        if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
+        return networkCallback.networkRequest;
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityThread.java b/core/java/android/net/ConnectivityThread.java
index 0b218e7..55c3402 100644
--- a/core/java/android/net/ConnectivityThread.java
+++ b/core/java/android/net/ConnectivityThread.java
@@ -27,30 +27,25 @@
  * @hide
  */
 public final class ConnectivityThread extends HandlerThread {
-
-    // A class implementing the lazy holder idiom: the unique static instance
-    // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by
-    // the language specs) the first time that Singleton is referenced in get()
-    // or getInstanceLooper().
-    private static class Singleton {
-        private static final ConnectivityThread INSTANCE = createInstance();
-    }
+    private static ConnectivityThread sInstance;
 
     private ConnectivityThread() {
         super("ConnectivityThread");
     }
 
-    private static ConnectivityThread createInstance() {
-        ConnectivityThread t = new ConnectivityThread();
-        t.start();
-        return t;
+    private static synchronized ConnectivityThread getInstance() {
+        if (sInstance == null) {
+            sInstance = new ConnectivityThread();
+            sInstance.start();
+        }
+        return sInstance;
     }
 
     public static ConnectivityThread get() {
-        return Singleton.INSTANCE;
+        return getInstance();
     }
 
     public static Looper getInstanceLooper() {
-        return Singleton.INSTANCE.getLooper();
+        return getInstance().getLooper();
     }
 }
diff --git a/core/java/android/net/EventLogTags.logtags b/core/java/android/net/EventLogTags.logtags
deleted file mode 100644
index d5ed014..0000000
--- a/core/java/android/net/EventLogTags.logtags
+++ /dev/null
@@ -1,6 +0,0 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
-
-option java_package android.net
-
-50080 ntp_success (server|3),(rtt|2),(offset|2)
-50081 ntp_failure (server|3),(msg|3)
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index d36b766..8f634bb 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -23,8 +23,7 @@
 interface IIpConnectivityMetrics {
 
     /**
-     * @return the number of remaining available slots in buffer,
-     * or -1 if the event was dropped due to rate limiting.
+     * @return number of remaining available slots in buffer.
      */
     int logEvent(in ConnectivityMetricsEvent event);
 }
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 9464222..e1eaf00 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -87,9 +87,9 @@
     {
         LocalSocketImpl acceptedImpl = new LocalSocketImpl();
 
-        impl.accept (acceptedImpl);
+        impl.accept(acceptedImpl);
 
-        return new LocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
+        return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
     }
 
     /**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index e14facb1..d9ad74b 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -73,19 +73,27 @@
         this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
         isBound = true;
         isConnected = true;
+        implCreated = true;
     }
 
-    /**
-     * for use with AndroidServerSocket
-     * @param impl a SocketImpl
-     */
-    /*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
+    private LocalSocket(LocalSocketImpl impl, int sockType) {
         this.impl = impl;
         this.sockType = sockType;
         this.isConnected = false;
         this.isBound = false;
     }
 
+    /**
+     * for use with LocalServerSocket.accept()
+     */
+    static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) {
+        LocalSocket socket = new LocalSocket(impl, sockType);
+        socket.isConnected = true;
+        socket.isBound = true;
+        socket.implCreated = true;
+        return socket;
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
@@ -216,11 +224,11 @@
         implCreateIfNeeded();
         impl.shutdownOutput();
     }
-    
+
     public void setReceiveBufferSize(int size) throws IOException {
         impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
     }
-    
+
     public int getReceiveBufferSize() throws IOException {
         return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
     }
@@ -228,7 +236,7 @@
     public void setSoTimeout(int n) throws IOException {
         impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(n));
     }
-    
+
     public int getSoTimeout() throws IOException {
         return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
     }
@@ -236,7 +244,7 @@
     public void setSendBufferSize(int n) throws IOException {
         impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(n));
     }
-    
+
     public int getSendBufferSize() throws IOException {
         return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
     }
@@ -321,5 +329,5 @@
      */
     public FileDescriptor getFileDescriptor() {
         return impl.getFileDescriptor();
-    }    
+    }
 }
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 0f0e9c4..d8f7821 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -235,29 +235,29 @@
      * @throws IOException
      */
     public void create(int sockType) throws IOException {
-        // no error if socket already created
-        // need this for LocalServerSocket.accept()
-        if (fd == null) {
-            int osType;
-            switch (sockType) {
-                case LocalSocket.SOCKET_DGRAM:
-                    osType = OsConstants.SOCK_DGRAM;
-                    break;
-                case LocalSocket.SOCKET_STREAM:
-                    osType = OsConstants.SOCK_STREAM;
-                    break;
-                case LocalSocket.SOCKET_SEQPACKET:
-                    osType = OsConstants.SOCK_SEQPACKET;
-                    break;
-                default:
-                    throw new IllegalStateException("unknown sockType");
-            }
-            try {
-                fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
-                mFdCreatedInternally = true;
-            } catch (ErrnoException e) {
-                e.rethrowAsIOException();
-            }
+        if (fd != null) {
+            throw new IOException("LocalSocketImpl already has an fd");
+        }
+
+        int osType;
+        switch (sockType) {
+            case LocalSocket.SOCKET_DGRAM:
+                osType = OsConstants.SOCK_DGRAM;
+                break;
+            case LocalSocket.SOCKET_STREAM:
+                osType = OsConstants.SOCK_STREAM;
+                break;
+            case LocalSocket.SOCKET_SEQPACKET:
+                osType = OsConstants.SOCK_SEQPACKET;
+                break;
+            default:
+                throw new IllegalStateException("unknown sockType");
+        }
+        try {
+            fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
+            mFdCreatedInternally = true;
+        } catch (ErrnoException e) {
+            e.rethrowAsIOException();
         }
     }
 
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index c704ef0..d570e66 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -171,8 +171,7 @@
         String subscriberId = null;
         String networkId = null;
         boolean roaming = false;
-        boolean metered = !state.networkCapabilities.hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        boolean metered = false;
 
         if (isNetworkTypeMobile(type)) {
             if (state.subscriberId == null) {
@@ -186,6 +185,9 @@
             subscriberId = state.subscriberId;
             roaming = state.networkInfo.isRoaming();
 
+            metered = !state.networkCapabilities.hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
         } else if (type == TYPE_WIFI) {
             if (state.networkId != null) {
                 networkId = state.networkId;
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 1825956..9a6dca0 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -27,7 +27,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -44,19 +43,11 @@
  * <p>A network scorer is any application which:
  * <ul>
  * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
- * <li>Includes a receiver for {@link #ACTION_SCORE_NETWORKS} guarded by the
- *     {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission which scores
- *     networks and (eventually) calls {@link #updateScores} with the results. If this receiver
- *     specifies an android:label attribute, this label will be used when referring to the
- *     application throughout system settings; otherwise, the application label will be used.
+ * <li>Include a Service for the {@link #ACTION_RECOMMEND_NETWORKS} action
+ *     protected by the {@link android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE}
+ *     permission.
  * </ul>
  *
- * <p>The system keeps track of an active scorer application; at any time, only this application
- * will receive {@link #ACTION_SCORE_NETWORKS} broadcasts and will be permitted to call
- * {@link #updateScores}. Applications may determine the current active scorer with
- * {@link #getActiveScorerPackage()} and request to change the active scorer by sending an
- * {@link #ACTION_CHANGE_ACTIVE} broadcast with another scorer.
- *
  * @hide
  */
 @SystemApi
@@ -263,12 +254,9 @@
     /**
      * Request scoring for networks.
      *
-     * <p>Note that this is just a helper method to assemble the broadcast, and will run in the
-     * calling process.
-     *
      * @return true if the broadcast was sent, or false if there is no active scorer.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
      * @hide
      */
     public boolean requestScores(NetworkKey[] networks) throws SecurityException {
@@ -285,7 +273,7 @@
      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
      * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE.
      * @hide
@@ -302,7 +290,7 @@
      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
      * @param filterType the {@link CacheUpdateFilter} to apply
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
      * @hide
      */
@@ -321,7 +309,7 @@
      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
      * @hide
      */
@@ -342,7 +330,8 @@
      *                request details
      * @return a {@link RecommendationResult} instance containing the recommended network
      *         to connect to
-     * @throws SecurityException
+     * @throws SecurityException if the caller does not hold the
+     *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
      */
     public RecommendationResult requestRecommendation(RecommendationRequest request)
             throws SecurityException {
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 4282ca7..ec0bb25d 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -231,13 +231,7 @@
         if (defaultApp == null) {
             return false;
         }
-        if (callingUid != defaultApp.packageUid) {
-            return false;
-        }
-        // 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 mContext.checkCallingPermission(Manifest.permission.SCORE_NETWORKS) ==
-                PackageManager.PERMISSION_GRANTED;
+        return callingUid == defaultApp.packageUid;
     }
 
     private boolean isNetworkRecommendationsDisabled() {
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 77ce65b..25806fa 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -68,18 +68,11 @@
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
 
-    /** {@link #metered} value to account for all metered states. */
-    public static final int METERED_ALL = -1;
-    /** {@link #metered} value where native, unmetered data is accounted. */
-    public static final int METERED_NO = 0;
-    /** {@link #metered} value where metered data is accounted. */
-    public static final int METERED_YES = 1;
-
-    /** {@link #roaming} value to account for all roaming states. */
+    /** {@link #set} value for all roaming values. */
     public static final int ROAMING_ALL = -1;
-    /** {@link #roaming} value where native, non-roaming data is accounted. */
+    /** {@link #set} value where native, non-roaming data is accounted. */
     public static final int ROAMING_NO = 0;
-    /** {@link #roaming} value where roaming data is accounted. */
+    /** {@link #set} value where roaming data is accounted. */
     public static final int ROAMING_YES = 1;
 
     // TODO: move fields to "mVariable" notation
@@ -95,7 +88,6 @@
     private int[] uid;
     private int[] set;
     private int[] tag;
-    private int[] metered;
     private int[] roaming;
     private long[] rxBytes;
     private long[] rxPackets;
@@ -113,12 +105,6 @@
          * to disk. We merge in the correct value when reporting this value to clients of
          * getSummary().
          */
-        public int metered;
-        /**
-         * Note that this is only populated w/ the default value when read from /proc or written
-         * to disk. We merge in the correct value when reporting this value to clients of
-         * getSummary().
-         */
         public int roaming;
         public long rxBytes;
         public long rxPackets;
@@ -137,17 +123,16 @@
 
         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
                 long txBytes, long txPackets, long operations) {
-            this(iface, uid, set, tag, METERED_NO, ROAMING_NO, rxBytes, rxPackets, txBytes,
-                    txPackets, operations);
+            this(iface, uid, set, tag, ROAMING_NO, rxBytes, rxPackets, txBytes, txPackets,
+                    operations);
         }
 
-        public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
-                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+        public Entry(String iface, int uid, int set, int tag, int roaming, long rxBytes,
+                long rxPackets, long txBytes, long txPackets, long operations) {
             this.iface = iface;
             this.uid = uid;
             this.set = set;
             this.tag = tag;
-            this.metered = metered;
             this.roaming = roaming;
             this.rxBytes = rxBytes;
             this.rxPackets = rxPackets;
@@ -180,7 +165,6 @@
             builder.append(" uid=").append(uid);
             builder.append(" set=").append(setToString(set));
             builder.append(" tag=").append(tagToString(tag));
-            builder.append(" metered=").append(meteredToString(metered));
             builder.append(" roaming=").append(roamingToString(roaming));
             builder.append(" rxBytes=").append(rxBytes);
             builder.append(" rxPackets=").append(rxPackets);
@@ -194,18 +178,13 @@
         public boolean equals(Object o) {
             if (o instanceof Entry) {
                 final Entry e = (Entry) o;
-                return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
-                        && roaming == e.roaming && rxBytes == e.rxBytes && rxPackets == e.rxPackets
-                        && txBytes == e.txBytes && txPackets == e.txPackets
-                        && operations == e.operations && iface.equals(e.iface);
+                return uid == e.uid && set == e.set && tag == e.tag && roaming == e.roaming
+                        && rxBytes == e.rxBytes && rxPackets == e.rxPackets && txBytes == e.txBytes
+                        && txPackets == e.txPackets && operations == e.operations
+                        && iface.equals(e.iface);
             }
             return false;
         }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(uid, set, tag, metered, roaming, iface);
-        }
     }
 
     public NetworkStats(long elapsedRealtime, int initialSize) {
@@ -217,7 +196,6 @@
             this.uid = new int[initialSize];
             this.set = new int[initialSize];
             this.tag = new int[initialSize];
-            this.metered = new int[initialSize];
             this.roaming = new int[initialSize];
             this.rxBytes = new long[initialSize];
             this.rxPackets = new long[initialSize];
@@ -231,7 +209,6 @@
             this.uid = EmptyArray.INT;
             this.set = EmptyArray.INT;
             this.tag = EmptyArray.INT;
-            this.metered = EmptyArray.INT;
             this.roaming = EmptyArray.INT;
             this.rxBytes = EmptyArray.LONG;
             this.rxPackets = EmptyArray.LONG;
@@ -249,7 +226,6 @@
         uid = parcel.createIntArray();
         set = parcel.createIntArray();
         tag = parcel.createIntArray();
-        metered = parcel.createIntArray();
         roaming = parcel.createIntArray();
         rxBytes = parcel.createLongArray();
         rxPackets = parcel.createLongArray();
@@ -267,7 +243,6 @@
         dest.writeIntArray(uid);
         dest.writeIntArray(set);
         dest.writeIntArray(tag);
-        dest.writeIntArray(metered);
         dest.writeIntArray(roaming);
         dest.writeLongArray(rxBytes);
         dest.writeLongArray(rxPackets);
@@ -302,11 +277,10 @@
     }
 
     @VisibleForTesting
-    public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming,
+    public NetworkStats addValues(String iface, int uid, int set, int tag, int roaming,
             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         return addValues(new Entry(
-                iface, uid, set, tag, metered, roaming, rxBytes, rxPackets, txBytes, txPackets,
-                operations));
+                iface, uid, set, tag, roaming, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
@@ -320,7 +294,6 @@
             uid = Arrays.copyOf(uid, newLength);
             set = Arrays.copyOf(set, newLength);
             tag = Arrays.copyOf(tag, newLength);
-            metered = Arrays.copyOf(metered, newLength);
             roaming = Arrays.copyOf(roaming, newLength);
             rxBytes = Arrays.copyOf(rxBytes, newLength);
             rxPackets = Arrays.copyOf(rxPackets, newLength);
@@ -334,7 +307,6 @@
         uid[size] = entry.uid;
         set[size] = entry.set;
         tag[size] = entry.tag;
-        metered[size] = entry.metered;
         roaming[size] = entry.roaming;
         rxBytes[size] = entry.rxBytes;
         rxPackets[size] = entry.rxPackets;
@@ -355,7 +327,6 @@
         entry.uid = uid[i];
         entry.set = set[i];
         entry.tag = tag[i];
-        entry.metered = metered[i];
         entry.roaming = roaming[i];
         entry.rxBytes = rxBytes[i];
         entry.rxPackets = rxPackets[i];
@@ -410,8 +381,7 @@
      * also be used to subtract values from existing rows.
      */
     public NetworkStats combineValues(Entry entry) {
-        final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
-                entry.roaming);
+        final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.roaming);
         if (i == -1) {
             // only create new entry when positive contribution
             addValues(entry);
@@ -439,11 +409,10 @@
     /**
      * Find first stats index that matches the requested parameters.
      */
-    public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming) {
+    public int findIndex(String iface, int uid, int set, int tag, int roaming) {
         for (int i = 0; i < size; i++) {
             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
-                    && metered == this.metered[i] && roaming == this.roaming[i]
-                    && Objects.equals(iface, this.iface[i])) {
+                    && roaming == this.roaming[i] && Objects.equals(iface, this.iface[i])) {
                 return i;
             }
         }
@@ -455,7 +424,7 @@
      * search around the hinted index as an optimization.
      */
     @VisibleForTesting
-    public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
+    public int findIndexHinted(String iface, int uid, int set, int tag, int roaming,
             int hintIndex) {
         for (int offset = 0; offset < size; offset++) {
             final int halfOffset = offset / 2;
@@ -469,8 +438,7 @@
             }
 
             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
-                    && metered == this.metered[i] && roaming == this.roaming[i]
-                    && Objects.equals(iface, this.iface[i])) {
+                    && roaming == this.roaming[i] && Objects.equals(iface, this.iface[i])) {
                 return i;
             }
         }
@@ -484,7 +452,7 @@
      */
     public void spliceOperationsFrom(NetworkStats stats) {
         for (int i = 0; i < size; i++) {
-            final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i]);
+            final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], roaming[i]);
             if (j == -1) {
                 operations[i] = 0;
             } else {
@@ -574,7 +542,6 @@
         entry.uid = limitUid;
         entry.set = SET_ALL;
         entry.tag = TAG_NONE;
-        entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
         entry.rxBytes = 0;
         entry.rxPackets = 0;
@@ -670,12 +637,11 @@
             entry.uid = left.uid[i];
             entry.set = left.set[i];
             entry.tag = left.tag[i];
-            entry.metered = left.metered[i];
             entry.roaming = left.roaming[i];
 
             // find remote row that matches, and subtract
             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
-                    entry.metered, entry.roaming, i);
+                    entry.roaming, i);
             if (j == -1) {
                 // newly appearing row, return entire value
                 entry.rxBytes = left.rxBytes[i];
@@ -721,7 +687,6 @@
         entry.uid = UID_ALL;
         entry.set = SET_ALL;
         entry.tag = TAG_NONE;
-        entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
         entry.operations = 0L;
 
@@ -751,7 +716,6 @@
         entry.iface = IFACE_ALL;
         entry.set = SET_ALL;
         entry.tag = TAG_NONE;
-        entry.metered = METERED_ALL;
         entry.roaming = ROAMING_ALL;
 
         for (int i = 0; i < size; i++) {
@@ -798,7 +762,6 @@
             pw.print(" uid="); pw.print(uid[i]);
             pw.print(" set="); pw.print(setToString(set[i]));
             pw.print(" tag="); pw.print(tagToString(tag[i]));
-            pw.print(" metered="); pw.print(meteredToString(metered[i]));
             pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
             pw.print(" rxPackets="); pw.print(rxPackets[i]);
@@ -867,22 +830,6 @@
     }
 
     /**
-     * Return text description of {@link #metered} value.
-     */
-    public static String meteredToString(int metered) {
-        switch (metered) {
-            case METERED_ALL:
-                return "ALL";
-            case METERED_NO:
-                return "NO";
-            case METERED_YES:
-                return "YES";
-            default:
-                return "UNKNOWN";
-        }
-    }
-
-    /**
      * Return text description of {@link #roaming} value.
      */
     public static String roamingToString(int roaming) {
@@ -957,8 +904,7 @@
         if (pool.isEmpty()) {
             return true;
         }
-        Entry moved =
-                addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
+        Entry moved = addTrafficToApplications(tunIface,  underlyingIface, tunIfaceTotal, pool);
         deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
 
         if (!moved.isEmpty()) {
@@ -973,9 +919,9 @@
      * Initializes the data used by the migrateTun() method.
      *
      * This is the first pass iteration which does the following work:
-     * (1) Adds up all the traffic through the tunUid's underlyingIface
+     * (1) Adds up all the traffic through tun0.
+     * (2) Adds up all the traffic through the tunUid's underlyingIface
      *     (both foreground and background).
-     * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
      */
     private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
             Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
@@ -995,9 +941,8 @@
                 underlyingIfaceTotal.add(recycle);
             }
 
-            if (recycle.uid != tunUid && recycle.tag == TAG_NONE
-                    && Objects.equals(tunIface, recycle.iface)) {
-                // Add up all tunIface traffic excluding traffic from the vpn app itself.
+            if (recycle.tag == TAG_NONE && Objects.equals(tunIface, recycle.iface)) {
+                // Add up all tunIface traffic.
                 tunIfaceTotal.add(recycle);
             }
         }
@@ -1013,15 +958,13 @@
         return pool;
     }
 
-    private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface,
+    private Entry addTrafficToApplications(String tunIface, String underlyingIface,
             Entry tunIfaceTotal, Entry pool) {
         Entry moved = new Entry();
         Entry tmpEntry = new Entry();
         tmpEntry.iface = underlyingIface;
         for (int i = 0; i < size; i++) {
-            // the vpn app is excluded from the redistribution but all moved traffic will be
-            // deducted from the vpn app (see deductTrafficFromVpnApp below).
-            if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) {
+            if (Objects.equals(iface[i], tunIface)) {
                 if (tunIfaceTotal.rxBytes > 0) {
                     tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
                 } else {
@@ -1051,7 +994,6 @@
                 tmpEntry.uid = uid[i];
                 tmpEntry.tag = tag[i];
                 tmpEntry.set = set[i];
-                tmpEntry.metered = metered[i];
                 tmpEntry.roaming = roaming[i];
                 combineValues(tmpEntry);
                 if (tag[i] == TAG_NONE) {
@@ -1071,25 +1013,24 @@
         moved.set = SET_DBG_VPN_OUT;
         moved.tag = TAG_NONE;
         moved.iface = underlyingIface;
-        moved.metered = METERED_ALL;
         moved.roaming = ROAMING_ALL;
         combineValues(moved);
 
         // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
         // the TAG_NONE traffic.
         //
-        // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO,
-        // which should be the case as it comes directly from the /proc file. We only blend in the
+        // Relies on the fact that the underlying traffic only has state ROAMING_NO, which
+        // should be the case as it comes directly from the /proc file. We only blend in the
         // roaming data after applying these adjustments, by checking the NetworkIdentity of the
         // underlying iface.
         int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO);
+                ROAMING_NO);
         if (idxVpnBackground != -1) {
             tunSubtract(idxVpnBackground, this, moved);
         }
 
         int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
-                METERED_NO, ROAMING_NO);
+                ROAMING_NO);
         if (idxVpnForeground != -1) {
             tunSubtract(idxVpnForeground, this, moved);
         }
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index a96f90d..d013f64 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -41,6 +41,7 @@
      * Builder class for constructing {@link RecommendationRequest} instances.
      * @hide
      */
+    @SystemApi
     public static final class Builder {
         private ScanResult[] mScanResults;
         private WifiConfiguration mCurrentConfig;
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 94e5187..7e3dd77 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Bundle;
@@ -24,6 +25,8 @@
 
 import java.lang.Math;
 import java.lang.UnsupportedOperationException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -33,6 +36,15 @@
  */
 @SystemApi
 public class ScoredNetwork implements Parcelable {
+
+  /**
+     * Key used with the {@link #attributes} bundle to define the badging curve.
+     *
+     * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link
+     * Badging} enums.
+     */
+    public static final String ATTRIBUTES_KEY_BADGING_CURVE =
+            "android.net.attributes.key.BADGING_CURVE";
     /**
      * Extra used with {@link #attributes} to specify whether the
      * network is believed to have a captive portal.
@@ -58,6 +70,15 @@
     public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
             "android.net.attributes.key.RANKING_SCORE_OFFSET";
 
+    @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Badging {}
+
+    public static final int BADGING_NONE = 0;
+    public static final int BADGING_SD = 10;
+    public static final int BADGING_HD = 20;
+    public static final int BADGING_4K = 30;
+
     /** A {@link NetworkKey} uniquely identifying this network. */
     public final NetworkKey networkKey;
 
@@ -249,6 +270,25 @@
         }
     }
 
+    /**
+     * Return the {@link Badging} enum for this network for the given RSSI, derived from the
+     * badging curve.
+     *
+     * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned.
+     *
+     * @param rssi The rssi level for which the badge should be calculated
+     */
+    @Badging
+    public int calculateBadge(int rssi) {
+        if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) {
+            RssiCurve badgingCurve =
+                    attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE);
+            return badgingCurve.lookupScore(rssi);
+        }
+
+        return BADGING_NONE;
+    }
+
     public static final Parcelable.Creator<ScoredNetwork> CREATOR =
             new Parcelable.Creator<ScoredNetwork>() {
                 @Override
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index cea56b5..cf9243f 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -36,7 +36,8 @@
  * }
  * </pre>
  */
-public class SntpClient {
+public class SntpClient
+{
     private static final String TAG = "SntpClient";
     private static final boolean DBG = true;
 
@@ -87,7 +88,6 @@
         try {
             address = InetAddress.getByName(host);
         } catch (Exception e) {
-            EventLogTags.writeNtpFailure(host, e.toString());
             if (DBG) Log.d(TAG, "request time failed: " + e);
             return false;
         }
@@ -142,7 +142,6 @@
             //             = (transit + skew - transit + skew)/2
             //             = (2 * skew)/2 = skew
             long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
-            EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset);
             if (DBG) {
                 Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
                         "clock offset: " + clockOffset + "ms");
@@ -154,7 +153,6 @@
             mNtpTimeReference = responseTicks;
             mRoundTripTime = roundTripTime;
         } catch (Exception e) {
-            EventLogTags.writeNtpFailure(address.toString(), e.toString());
             if (DBG) Log.d(TAG, "request time failed: " + e);
             return false;
         } finally {
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 0667495..3b3fa69 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -42,15 +42,6 @@
     public static final int NETWORK_DISCONNECTED         = 7;
 
     /** {@hide} */
-    public static final int NETWORK_FIRST_VALIDATION_SUCCESS      = 8;
-    /** {@hide} */
-    public static final int NETWORK_REVALIDATION_SUCCESS          = 9;
-    /** {@hide} */
-    public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
-    /** {@hide} */
-    public static final int NETWORK_REVALIDATION_PORTAL_FOUND     = 11;
-
-    /** {@hide} */
     @IntDef(value = {
             NETWORK_CONNECTED,
             NETWORK_VALIDATED,
@@ -59,10 +50,6 @@
             NETWORK_LINGER,
             NETWORK_UNLINGER,
             NETWORK_DISCONNECTED,
-            NETWORK_FIRST_VALIDATION_SUCCESS,
-            NETWORK_REVALIDATION_SUCCESS,
-            NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
-            NETWORK_REVALIDATION_PORTAL_FOUND,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index a724ec1..1a31b56 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -44,8 +44,10 @@
     public static final int DNS_FAILURE = 0;
     public static final int DNS_SUCCESS = 1;
 
-    private static final int FIRST_VALIDATION  = 1 << 8;
-    private static final int REVALIDATION      = 2 << 8;
+    /** {@hide} */
+    @IntDef(value = {PROBE_DNS, PROBE_HTTP, PROBE_HTTPS, PROBE_PAC})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProbeType {}
 
     /** {@hide} */
     @IntDef(value = {DNS_FAILURE, DNS_SUCCESS})
@@ -54,17 +56,12 @@
 
     public final int netId;
     public final long durationMs;
-    // probeType byte format (MSB to LSB):
-    // byte 0: unused
-    // byte 1: unused
-    // byte 2: 0 = UNKNOWN, 1 = FIRST_VALIDATION, 2 = REVALIDATION
-    // byte 3: PROBE_* constant
-    public final int probeType;
+    public final @ProbeType int probeType;
     public final @ReturnCode int returnCode;
 
     /** {@hide} */
     public ValidationProbeEvent(
-            int netId, long durationMs, int probeType, @ReturnCode int returnCode) {
+            int netId, long durationMs, @ProbeType int probeType, @ReturnCode int returnCode) {
         this.netId = netId;
         this.durationMs = durationMs;
         this.probeType = probeType;
@@ -103,18 +100,8 @@
     };
 
     /** @hide */
-    public static int makeProbeType(int probeType, boolean firstValidation) {
-        return (probeType & 0xff) | (firstValidation ? FIRST_VALIDATION : REVALIDATION);
-    }
-
-    /** @hide */
     public static String getProbeName(int probeType) {
-        return Decoder.constants.get(probeType & 0xff, "PROBE_???");
-    }
-
-    /** @hide */
-    public static String getValidationStage(int probeType) {
-        return Decoder.constants.get(probeType & 0xff00, "UNKNOWN");
+        return Decoder.constants.get(probeType, "PROBE_???");
     }
 
     public static void logEvent(int netId, long durationMs, int probeType, int returnCode) {
@@ -122,13 +109,12 @@
 
     @Override
     public String toString() {
-        return String.format("ValidationProbeEvent(%d, %s:%d %s, %dms)", netId,
-                getProbeName(probeType), returnCode, getValidationStage(probeType), durationMs);
+        return String.format("ValidationProbeEvent(%d, %s:%d, %dms)",
+                netId, getProbeName(probeType), returnCode, durationMs);
     }
 
     final static class Decoder {
         static final SparseArray<String> constants = MessageUtils.findMessageNames(
-                new Class[]{ValidationProbeEvent.class},
-                new String[]{"PROBE_", "FIRST_", "REVALIDATION"});
+                new Class[]{ValidationProbeEvent.class}, new String[]{"PROBE_"});
     }
 }
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 33d12a3..86bd502 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -103,11 +103,11 @@
  * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on
  * {@link DiscoveryListener#onServiceLost}.
  *
- * <p> Once the peer application discovers the "Example" http service, and either needs to read the
- * attributes of the service or wants to receive data from the "Example" application, it can
- * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port
- * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a
- * failure is notified on {@link ResolveListener#onResolveFailed}.
+ * <p> Once the peer application discovers the "Example" http srevice, and needs to receive data
+ * from the "Example" application, it can initiate a resolve with {@link #resolveService} to
+ * resolve the host and port details for the purpose of establishing a connection. A successful
+ * resolve is notified on {@link ResolveListener#onServiceResolved} and a failure is notified
+ * on {@link ResolveListener#onResolveFailed}.
  *
  * Applications can reserve for a service type at
  * http://www.iana.org/form/ports-service. Existing services can be found at
diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java
index 7b845be..4a06fb1 100644
--- a/core/java/android/net/nsd/NsdServiceInfo.java
+++ b/core/java/android/net/nsd/NsdServiceInfo.java
@@ -250,8 +250,7 @@
     }
 
     /**
-     * Retrieve attributes as a map of String keys to byte[] values. The attributes map is only
-     * valid for a resolved service.
+     * Retrive attributes as a map of String keys to byte[] values.
      *
      * <p> The returned map is unmodifiable; changes must be made through {@link #setAttribute} and
      * {@link #removeAttribute}.
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 151239b..f6edee0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -783,7 +783,7 @@
      */
     public static boolean isBuildConsistent() {
         // Don't care on eng builds.  Incremental build may trigger false negative.
-        if ("eng".equals(TYPE)) return true;
+        if (IS_ENG) return true;
 
         final String system = SystemProperties.get("ro.build.fingerprint");
         final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
@@ -847,6 +847,10 @@
     public static final boolean IS_DEBUGGABLE =
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
+    /** {@hide} */
+    public static final boolean IS_ENG =
+            "eng".equals(getString("ro.build.type"));
+
     /**
      * Specifies whether the permissions needed by a legacy app should be
      * reviewed before any of its components can run. A legacy app is one
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 481b2dc..e025494 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import java.util.ArrayList;
+import java.util.NoSuchElementException;
 import libcore.util.NativeAllocationRegistry;
 
 /** @hide */
@@ -33,19 +34,24 @@
                 mNativeContext);
     }
 
+    @Override
     public final native void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public abstract void onTransact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public native final void registerService(
             ArrayList<String> interfaceChain,
-            String serviceName);
+            String serviceName)
+        throws RemoteException;
 
     public static native final IHwBinder getService(
             String iface,
-            String serviceName);
+            String serviceName)
+        throws RemoteException, NoSuchElementException;
 
     // Returns address of the "freeFunction".
     private static native final long native_init();
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 83866b3..2f89ce6 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -32,12 +32,18 @@
                 mNativeContext);
     }
 
+    @Override
     public IHwInterface queryLocalInterface(String descriptor) {
         return null;
     }
 
+    @Override
     public native final void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
+
+    public native boolean linkToDeath(DeathRecipient recipient, long cookie);
+    public native boolean unlinkToDeath(DeathRecipient recipient);
 
     private static native final long native_init();
 
@@ -52,5 +58,9 @@
                 128 /* size */);
     }
 
+    private static final void sendDeathNotice(DeathRecipient recipient, long cookie) {
+        recipient.serviceDied(cookie);
+    }
+
     private long mNativeContext;
 }
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 76e881e..619f4dc 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -23,7 +23,19 @@
     public static final int FLAG_ONEWAY = 1;
 
     public void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public IHwInterface queryLocalInterface(String descriptor);
+
+    /**
+     * Interface for receiving a callback when the process hosting a service
+     * has gone away.
+     */
+    public interface DeathRecipient {
+        public void serviceDied(long cookie);
+    }
+
+    public boolean linkToDeath(DeathRecipient recipient, long cookie);
+    public boolean unlinkToDeath(DeathRecipient recipient);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9cd1a42..d6688e3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -184,6 +184,11 @@
      */
     public static final int LAST_SHARED_APPLICATION_GID = 59999;
 
+    /** {@hide} */
+    public static final int FIRST_APPLICATION_CACHE_GID = 20000;
+    /** {@hide} */
+    public static final int LAST_APPLICATION_CACHE_GID = 29999;
+
     /**
      * Standard priority of application threads.
      * Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b3f4453..535a05a 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -213,6 +213,15 @@
     }
 
     /**
+     * Returns the cache GID for a given UID or appId.
+     * @hide
+     */
+    public static int getCacheAppGid(int id) {
+        return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
+                - Process.FIRST_APPLICATION_UID;
+    }
+
+    /**
      * Generate a text representation of the uid, breaking out its individual
      * components -- user, app, isolated, etc.
      * @hide
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c5507b9..9252887 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -40,6 +40,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -49,6 +50,7 @@
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.ref.WeakReference;
@@ -930,24 +932,38 @@
     }
 
     /** {@hide} */
-    public long getPrimaryStorageSize() {
+    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
         for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
             final long numberBlocks = readLong(path);
             if (numberBlocks > 0) {
-                return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE;
+                return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE));
             }
         }
-        return 0;
+        return null;
     }
 
-    private long readLong(String path) {
+
+    /** {@hide} */
+    public long getPrimaryStorageSize() {
+        final Pair<String, Long> pair = getPrimaryStoragePathAndSize();
+        return pair == null ? 0 : pair.second.longValue();
+    }
+
+    private static long readLong(String path) {
         try (final FileInputStream fis = new FileInputStream(path);
                 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
             return Long.parseLong(reader.readLine());
-        } catch (Exception e) {
-            Slog.w(TAG, "Could not read " + path, e);
+        } catch (FileNotFoundException e) {
+            // This is expected since we are trying to parse multiple paths.
+            Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e);
             return 0;
-        }
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e);
+            return 0;
+        } catch (Exception e) {
+            Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e);
+            return 0;
+       }
     }
 
     /** @removed */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e52983e..0f713c1 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6498,7 +6498,8 @@
             QS_TILES,
             DOZE_ENABLED,
             DOZE_PULSE_ON_PICK_UP,
-            DOZE_PULSE_ON_DOUBLE_TAP
+            DOZE_PULSE_ON_DOUBLE_TAP,
+            NFC_PAYMENT_DEFAULT_COMPONENT
         };
 
         /**
@@ -7172,13 +7173,6 @@
         */
        public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
 
-        /**
-         * Size of the event buffer for IP connectivity metrics.
-         * @hide
-         */
-        public static final String CONNECTIVITY_METRICS_BUFFER_SIZE =
-              "connectivity_metrics_buffer_size";
-
        /** {@hide} */
        public static final String NETSTATS_ENABLED = "netstats_enabled";
        /** {@hide} */
@@ -8035,45 +8029,11 @@
         public static final String PAC_CHANGE_DELAY = "pac_change_delay";
 
         /**
-         * Don't attempt to detect captive portals.
-         *
-         * @hide
-         */
-        public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
-
-        /**
-         * When detecting a captive portal, display a notification that
-         * prompts the user to sign in.
-         *
-         * @hide
-         */
-        public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
-
-        /**
-         * When detecting a captive portal, immediately disconnect from the
-         * network and do not reconnect to that network in the future.
-         *
-         * @hide
-         */
-        public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
-
-        /**
-         * What to do when connecting a network that presents a captive portal.
-         * Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
-         *
-         * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
-         * @hide
-         */
-        public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
-
-        /**
          * Setting to turn off captive portal detection. Feature is enabled by
          * default and the setting needs to be set to 0 to disable it.
          *
-         * @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection
          * @hide
          */
-        @Deprecated
         public static final String
                 CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2e0729b..67196ca 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -75,17 +75,6 @@
     private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
 
     /**
-     * System property used to enable or disable dirty regions invalidation.
-     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
-     * The default value of this property is assumed to be true.
-     *
-     * Possible values:
-     * "true", to enable partial invalidates
-     * "false", to disable partial invalidates
-     */
-    static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
-
-    /**
      * System property used to enable or disable hardware rendering profiling.
      * The default value of this property is assumed to be false.
      *
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index e0d589a..2d6f443 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -24,6 +24,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -38,70 +40,104 @@
     private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
     private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
 
+    /**
+     * Lock object that protects all other static members.
+     */
+    private static final Object sLock = new Object();
+
+    /**
+     * Instance that maintains the socket connection to the zygote. This is null if the zygote
+     * is not running or is not connected.
+     */
+    @GuardedBy("sLock")
     private static ZygoteProcess sZygote;
 
+    /**
+     * Information about the selected WebView package. This is set from #onWebViewProviderChanged().
+     */
+    @GuardedBy("sLock")
     private static PackageInfo sPackage;
 
+    /**
+     * Flag for whether multi-process WebView is enabled. If this is false, the zygote
+     * will not be started.
+     */
+    @GuardedBy("sLock")
     private static boolean sMultiprocessEnabled = false;
 
     public static ZygoteProcess getProcess() {
-        connectToZygoteIfNeeded();
-        return sZygote;
+        synchronized (sLock) {
+            connectToZygoteIfNeededLocked();
+            return sZygote;
+        }
     }
 
     public static String getPackageName() {
-        return sPackage.packageName;
+        synchronized (sLock) {
+            return sPackage.packageName;
+        }
     }
 
     public static boolean isMultiprocessEnabled() {
-        return sMultiprocessEnabled && sPackage != null;
+        synchronized (sLock) {
+            return sMultiprocessEnabled && sPackage != null;
+        }
     }
 
     public static void setMultiprocessEnabled(boolean enabled) {
-        sMultiprocessEnabled = enabled;
+        synchronized (sLock) {
+            sMultiprocessEnabled = enabled;
 
-        // When toggling between multi-process being on/off, start or stop the
-        // service. If it is enabled and the zygote is not yet started, bring up the service.
-        // Otherwise, bring down the service. The name may be null if the package
-        // information has not yet been resolved.
-        final String serviceName = getServiceName();
-        if (serviceName == null) return;
+            // When toggling between multi-process being on/off, start or stop the
+            // service. If it is enabled and the zygote is not yet started, bring up the service.
+            // Otherwise, bring down the service. The name may be null if the package
+            // information has not yet been resolved.
+            final String serviceName = getServiceNameLocked();
+            if (serviceName == null) return;
 
-        if (enabled && sZygote == null) {
-            SystemService.start(serviceName);
-        } else {
-            SystemService.stop(serviceName);
-            sZygote = null;
+            if (enabled && sZygote == null) {
+                SystemService.start(serviceName);
+            } else {
+                SystemService.stop(serviceName);
+                sZygote = null;
+            }
         }
     }
 
     public static void onWebViewProviderChanged(PackageInfo packageInfo) {
-        sPackage = packageInfo;
+        String serviceName;
+        synchronized (sLock) {
+            sPackage = packageInfo;
 
-        // If multi-process is not enabled, then do not start the zygote service.
-        if (!sMultiprocessEnabled) {
-            return;
+            // If multi-process is not enabled, then do not start the zygote service.
+            if (!sMultiprocessEnabled) {
+                return;
+            }
+
+            serviceName = getServiceNameLocked();
+            sZygote = null;
+
+            // The service may enter the RUNNING state before it opens the socket,
+            // so connectToZygoteIfNeededLocked() may still fail.
+            if (SystemService.isStopped(serviceName)) {
+                SystemService.start(serviceName);
+            } else {
+                SystemService.restart(serviceName);
+            }
+
+            try {
+                SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
+            } catch (TimeoutException e) {
+                Log.e(LOGTAG, "Timed out waiting for " + serviceName);
+                return;
+            }
+
+            connectToZygoteIfNeededLocked();
         }
-
-        final String serviceName = getServiceName();
-
-        if (SystemService.isStopped(serviceName)) {
-            SystemService.start(serviceName);
-        } else if (sZygote != null) {
-            SystemService.restart(serviceName);
-        }
-
-        try {
-            SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
-        } catch (TimeoutException e) {
-            Log.e(LOGTAG, "Timed out waiting for " + serviceName);
-            return;
-        }
-
-        connectToZygoteIfNeeded();
     }
 
-    private static String getServiceName() {
+    @GuardedBy("sLock")
+    private static String getServiceNameLocked() {
         if (sPackage == null)
             return null;
 
@@ -113,7 +149,8 @@
         return WEBVIEW_ZYGOTE_SERVICE_32;
     }
 
-    private static void connectToZygoteIfNeeded() {
+    @GuardedBy("sLock")
+    private static void connectToZygoteIfNeededLocked() {
         if (sZygote != null)
             return;
 
@@ -122,7 +159,7 @@
             return;
         }
 
-        final String serviceName = getServiceName();
+        final String serviceName = getServiceNameLocked();
         if (!SystemService.isRunning(serviceName)) {
             Log.e(LOGTAG, serviceName + " is not running");
             return;
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index d968e3c..a8a5549 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -62,6 +62,9 @@
             ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                     packagePath, libsPath);
 
+            // Add the APK to the Zygote's list of allowed files for children.
+            Zygote.nativeAllowFileAcrossFork(packagePath);
+
             // Once we have the classloader, look up the WebViewFactoryProvider implementation and
             // call preloadInZygote() on it to give it the opportunity to preload the native library
             // and perform any other initialisation work that should be shared among the children.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..293de3d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -153,6 +153,11 @@
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
     /**
+     * Lets children of the zygote inherit open file descriptors to this path.
+     */
+    native protected static void nativeAllowFileAcrossFork(String path);
+
+    /**
      * Zygote unmount storage space on initializing.
      * This method is called once.
      */
diff --git a/core/java/com/android/internal/util/TokenBucket.java b/core/java/com/android/internal/util/TokenBucket.java
deleted file mode 100644
index effb82b..0000000
--- a/core/java/com/android/internal/util/TokenBucket.java
+++ /dev/null
@@ -1,126 +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 com.android.internal.util;
-
-import android.os.SystemClock;
-
-import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
-import static com.android.internal.util.Preconditions.checkArgumentPositive;
-
-/**
- * A class useful for rate-limiting or throttling that stores and distributes tokens.
- *
- * A TokenBucket starts with a fixed capacity of tokens, an initial amount of tokens, and
- * a fixed filling period (in milliseconds).
- *
- * For every filling period, the bucket gains one token, up to its maximum capacity from
- * which point tokens simply overflow and are lost. Tokens can be obtained one by one or n by n.
- *
- * The available amount of tokens is computed lazily when the bucket state is inspected.
- * Therefore it is purely synchronous and does not involve any asynchronous activity.
- * It is not synchronized in any way and not a thread-safe object.
- */
-public class TokenBucket {
-
-    private final int mFillDelta; // Time in ms it takes to generate one token.
-    private final int mCapacity;  // Maximum number of tokens that can be stored.
-    private long mLastFill;       // Last time in ms the bucket generated tokens.
-    private int mAvailable;       // Current number of available tokens.
-
-    /**
-     * Create a new TokenBucket.
-     * @param deltaMs the time in milliseconds it takes to generate a new token.
-     * Must be strictly positive.
-     * @param capacity the maximum token capacity. Must be strictly positive.
-     * @param tokens the starting amount of token. Must be positive or zero.
-     */
-    public TokenBucket(int deltaMs, int capacity, int tokens) {
-        mFillDelta = checkArgumentPositive(deltaMs, "deltaMs must be strictly positive");
-        mCapacity = checkArgumentPositive(capacity, "capacity must be strictly positive");
-        mAvailable = Math.min(checkArgumentNonnegative(tokens), mCapacity);
-        mLastFill = scaledTime();
-    }
-
-    /**
-     * Create a new TokenBucket that starts completely filled.
-     * @param deltaMs the time in milliseconds it takes to generate a new token.
-     * Must be strictly positive.
-     * @param capacity the maximum token capacity. Must be strictly positive.
-     */
-    public TokenBucket(int deltaMs, int capacity) {
-        this(deltaMs, capacity, capacity);
-    }
-
-    /** Reset this TokenBucket and set its number of available tokens. */
-    public void reset(int tokens) {
-        checkArgumentNonnegative(tokens);
-        mAvailable = Math.min(tokens, mCapacity);
-        mLastFill = scaledTime();
-    }
-
-    /** Returns this TokenBucket maximum token capacity. */
-    public int capacity() {
-        return mCapacity;
-    }
-
-    /** Returns this TokenBucket currently number of available tokens. */
-    public int available() {
-        fill();
-        return mAvailable;
-    }
-
-    /** Returns true if this TokenBucket as one or more tokens available. */
-    public boolean has() {
-        fill();
-        return mAvailable > 0;
-    }
-
-    /** Consumes a token from this TokenBucket and returns true if a token is available. */
-    public boolean get() {
-        return (get(1) == 1);
-    }
-
-    /**
-     * Try to consume many tokens from this TokenBucket.
-     * @param n the number of tokens to consume.
-     * @return the number of tokens that were actually consumed.
-     */
-    public int get(int n) {
-        fill();
-        if (n <= 0) {
-            return 0;
-        }
-        if (n > mAvailable) {
-            int got = mAvailable;
-            mAvailable = 0;
-            return got;
-        }
-        mAvailable -= n;
-        return n;
-    }
-
-    private void fill() {
-        final long now = scaledTime();
-        final int diff = (int) (now - mLastFill);
-        mAvailable = Math.min(mCapacity, mAvailable + diff);
-        mLastFill = now;
-    }
-
-    private long scaledTime() {
-        return SystemClock.elapsedRealtime() / mFillDelta;
-    }
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 70e9004..d9d06c5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -179,6 +179,7 @@
     com_android_internal_util_VirtualRefBasePtr.cpp \
     com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \
     hwbinder/EphemeralStorage.cpp \
+    fd_utils.cpp \
 
 LOCAL_C_INCLUDES += \
     $(LOCAL_PATH)/include \
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8eb39e1..fbccfd5 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -33,6 +33,7 @@
 #include <unordered_map>
 #include <queue>
 
+#include <android-base/macros.h>
 #include <cutils/log.h>
 
 #include "JNIHelp.h"
@@ -704,10 +705,10 @@
     }
 
     jbyteArray jmsg = env->NewByteArray(msgLen);
-    jintArray jheader = env->NewIntArray(sizeof(header));
+    jintArray jheader = env->NewIntArray(arraysize(header));
 
     env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
-    env->SetIntArrayRegion(jheader, 0, sizeof(header), (jint *)header);
+    env->SetIntArrayRegion(jheader, 0, arraysize(header), (jint *)header);
 
     ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
           header[HEADER_FIELD_MSG_TYPE], header[HEADER_FIELD_APP_INSTANCE],
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 4976002..d30e6eb 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -457,6 +457,18 @@
 }
 
 static jint
+android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, jint device, jstring device_address, jstring device_name)
+{
+    const char *c_address = env->GetStringUTFChars(device_address, NULL);
+    const char *c_name = env->GetStringUTFChars(device_name, NULL);
+    int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device),
+                                          c_address, c_name));
+    env->ReleaseStringUTFChars(device_address, c_address);
+    env->ReleaseStringUTFChars(device_name, c_name);
+    return (jint) status;
+}
+
+static jint
 android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
 {
     return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
@@ -1757,6 +1769,7 @@
     {"newAudioSessionId",   "()I",      (void *)android_media_AudioSystem_newAudioSessionId},
     {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
     {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
+    {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
     {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
     {"setForceUse",         "(II)I",    (void *)android_media_AudioSystem_setForceUse},
     {"getForceUse",         "(I)I",     (void *)android_media_AudioSystem_getForceUse},
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index d6d4310..37b6df1 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -202,9 +202,7 @@
     msg.msg_control = cmsgbuf;
     msg.msg_controllen = sizeof(cmsgbuf);
 
-    do {
-        ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
-    } while (ret < 0 && errno == EINTR);
+    ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
 
     if (ret < 0 && errno == EPIPE) {
         // Treat this as an end of stream
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 2ae4a17..20bb885 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -39,6 +39,8 @@
 using android::AndroidRuntime;
 using android::hardware::hidl_vec;
 using android::hardware::hidl_string;
+template<typename T>
+using Return = android::hardware::Return<T>;
 
 #define PACKAGE_PATH    "android/os"
 #define CLASS_NAME      "HwBinder"
@@ -52,6 +54,8 @@
     jmethodID get;
 } gArrayListMethods;
 
+static jclass gErrorClass;
+
 static struct fields_t {
     jfieldID contextID;
     jmethodID onTransactID;
@@ -144,6 +148,22 @@
             replyObj.get(),
             flags);
 
+    if (env->ExceptionCheck()) {
+        jthrowable excep = env->ExceptionOccurred();
+        env->ExceptionDescribe();
+
+        if (env->IsInstanceOf(excep, gErrorClass)) {
+            /* It's an error */
+            LOG(ERROR) << "Forcefully exiting";
+            exit(1);
+        } else {
+            env->ExceptionClear();
+            LOG(ERROR) << "Uncaught exception!";
+        }
+
+        env->DeleteLocalRef(excep);
+    }
+
     status_t err = OK;
 
     if (!replyContext->wasSent()) {
@@ -239,25 +259,32 @@
     hidl_vec<hidl_string> interfaceChain;
     interfaceChain.setToExternal(strings, numInterfaces, true /* shouldOwn */);
 
-    using android::hidl::manager::V1_0::IServiceManager;
-
     sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+
     /* TODO(b/33440494) this is not right */
     sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpBase(binder);
-    bool ok = hardware::defaultServiceManager()->add(
-                interfaceChain,
-                serviceName,
-                base);
+
+    auto manager = hardware::defaultServiceManager();
+
+    if (manager == nullptr) {
+        LOG(ERROR) << "Could not get hwservicemanager.";
+        signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+        return;
+    }
+
+    Return<bool> ret = manager->add(interfaceChain, serviceName, base);
 
     env->ReleaseStringUTFChars(serviceNameObj, serviceName);
     serviceName = NULL;
 
+    bool ok = ret.isOk() && ret;
+
     if (ok) {
         LOG(INFO) << "Starting thread pool.";
         ::android::hardware::ProcessState::self()->startThreadPool();
     }
 
-    signalExceptionForError(env, (ok ? OK : UNKNOWN_ERROR));
+    signalExceptionForError(env, (ok ? OK : UNKNOWN_ERROR), true /* canThrowRemoteException */);
 }
 
 static jobject JHwBinder_native_getService(
@@ -289,15 +316,22 @@
               << serviceName
               << "'";
 
-    sp<hardware::IBinder> service;
-    hardware::defaultServiceManager()->get(
-            ifaceName,
-            serviceName,
-            [&service](sp<hidl::base::V1_0::IBase> out) {
-                service = hardware::toBinder<
-                        hidl::base::V1_0::IBase, hidl::base::V1_0::BpBase
-                    >(out);
-            });
+    auto manager = hardware::defaultServiceManager();
+
+    if (manager == nullptr) {
+        LOG(ERROR) << "Could not get hwservicemanager.";
+        signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+        return NULL;
+    }
+
+    Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceName, serviceName);
+
+    if (!ret.isOk()) {
+        signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+    }
+
+    sp<hardware::IBinder> service = hardware::toBinder<
+            hidl::base::V1_0::IBase, hidl::base::V1_0::BpBase>(ret);
 
     env->ReleaseStringUTFChars(ifaceNameObj, ifaceName);
     ifaceName = NULL;
@@ -338,6 +372,9 @@
     gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
     gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
 
+    jclass errorClass = FindClassOrDie(env, "java/lang/Error");
+    gErrorClass = MakeGlobalRefOrDie(env, errorClass);
+
     return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a10d807..b9d810a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -49,7 +49,7 @@
 
 } gFields;
 
-void signalExceptionForError(JNIEnv *env, status_t err) {
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException) {
     switch (err) {
         case OK:
             break;
@@ -114,8 +114,13 @@
 
         default:
         {
+            std::stringstream ss;
+            ss << "HwBinder Error: (" << err << ")";
+
             jniThrowException(
-                    env, "java/lang/RuntimeException", "Unknown error");
+                    env,
+                    canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
+                    ss.str().c_str());
 
             break;
         }
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index 708bbba..f81de9b 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -67,7 +67,7 @@
     DISALLOW_COPY_AND_ASSIGN(JHwParcel);
 };
 
-void signalExceptionForError(JNIEnv *env, status_t err);
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException = false);
 int register_android_os_HwParcel(JNIEnv *env);
 
 }  // namespace android
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 1d5d6d5..f2f8e52 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -25,6 +25,7 @@
 #include <JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <hidl/Status.h>
+#include <ScopedUtfChars.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include "core_jni_helpers.h"
@@ -38,26 +39,196 @@
 namespace android {
 
 static struct fields_t {
+    jclass proxy_class;
     jfieldID contextID;
     jmethodID constructID;
+    jmethodID sendDeathNotice;
+} gProxyOffsets;
 
-} gFields;
+static struct class_offsets_t
+{
+    jmethodID mGetName;
+} gClassOffsets;
+
+static JavaVM* jnienv_to_javavm(JNIEnv* env)
+{
+    JavaVM* vm;
+    return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
+}
+
+static JNIEnv* javavm_to_jnienv(JavaVM* vm)
+{
+    JNIEnv* env;
+    return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+}
+
+// ----------------------------------------------------------------------------
+class HwBinderDeathRecipient : public hardware::IBinder::DeathRecipient
+{
+public:
+    HwBinderDeathRecipient(JNIEnv* env, jobject object, jlong cookie, const sp<HwBinderDeathRecipientList>& list)
+        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
+          mObjectWeak(NULL), mCookie(cookie), mList(list)
+    {
+        // These objects manage their own lifetimes so are responsible for final bookkeeping.
+        // The list holds a strong reference to this object.
+        list->add(this);
+    }
+
+    void binderDied(const wp<hardware::IBinder>& who)
+    {
+        if (mObject != NULL) {
+            JNIEnv* env = javavm_to_jnienv(mVM);
+
+            env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice, mObject, mCookie);
+            if (env->ExceptionCheck()) {
+                ALOGE("Uncaught exception returned from death notification.");
+                env->ExceptionClear();
+            }
+
+            // Serialize with our containing HwBinderDeathRecipientList so that we can't
+            // delete the global ref on mObject while the list is being iterated.
+            sp<HwBinderDeathRecipientList> list = mList.promote();
+            if (list != NULL) {
+                AutoMutex _l(list->lock());
+
+                // Demote from strong ref to weak after binderDied() has been delivered,
+                // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
+                mObjectWeak = env->NewWeakGlobalRef(mObject);
+                env->DeleteGlobalRef(mObject);
+                mObject = NULL;
+            }
+        }
+    }
+
+    void clearReference()
+    {
+        sp<HwBinderDeathRecipientList> list = mList.promote();
+        if (list != NULL) {
+            list->remove(this);
+        } else {
+            ALOGE("clearReference() on JDR %p but DRL wp purged", this);
+        }
+    }
+
+    bool matches(jobject obj) {
+        bool result;
+        JNIEnv* env = javavm_to_jnienv(mVM);
+
+        if (mObject != NULL) {
+            result = env->IsSameObject(obj, mObject);
+        } else {
+            jobject me = env->NewLocalRef(mObjectWeak);
+            result = env->IsSameObject(obj, me);
+            env->DeleteLocalRef(me);
+        }
+        return result;
+    }
+
+    void warnIfStillLive() {
+        if (mObject != NULL) {
+            // Okay, something is wrong -- we have a hard reference to a live death
+            // recipient on the VM side, but the list is being torn down.
+            JNIEnv* env = javavm_to_jnienv(mVM);
+            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+            ScopedLocalRef<jstring> nameRef(env,
+                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
+            ScopedUtfChars nameUtf(env, nameRef.get());
+            if (nameUtf.c_str() != NULL) {
+                ALOGW("BinderProxy is being destroyed but the application did not call "
+                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
+                        "Releasing leaked death recipient: %s", nameUtf.c_str());
+            } else {
+                ALOGW("BinderProxy being destroyed; unable to get DR object name");
+                env->ExceptionClear();
+            }
+        }
+    }
+
+protected:
+    virtual ~HwBinderDeathRecipient()
+    {
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        if (mObject != NULL) {
+            env->DeleteGlobalRef(mObject);
+        } else {
+            env->DeleteWeakGlobalRef(mObjectWeak);
+        }
+    }
+
+private:
+    JavaVM* const mVM;
+    jobject mObject;
+    jweak mObjectWeak; // will be a weak ref to the same VM-side DeathRecipient after binderDied()
+    jlong mCookie;
+    wp<HwBinderDeathRecipientList> mList;
+};
+// ----------------------------------------------------------------------------
+
+HwBinderDeathRecipientList::HwBinderDeathRecipientList() {
+}
+
+HwBinderDeathRecipientList::~HwBinderDeathRecipientList() {
+    AutoMutex _l(mLock);
+
+    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
+        deathRecipient->warnIfStillLive();
+    }
+}
+
+void HwBinderDeathRecipientList::add(const sp<HwBinderDeathRecipient>& recipient) {
+    AutoMutex _l(mLock);
+
+    mList.push_back(recipient);
+}
+
+void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
+    AutoMutex _l(mLock);
+
+    List< sp<HwBinderDeathRecipient> >::iterator iter;
+    for (iter = mList.begin(); iter != mList.end(); iter++) {
+        if (*iter == recipient) {
+            mList.erase(iter);
+            return;
+        }
+    }
+}
+
+sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
+    AutoMutex _l(mLock);
+
+    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
+        if (deathRecipient->matches(recipient)) {
+            return deathRecipient;
+        }
+    }
+    return NULL;
+}
+
+Mutex& HwBinderDeathRecipientList::lock() {
+    return mLock;
+}
 
 // static
 void JHwRemoteBinder::InitClass(JNIEnv *env) {
-    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+    jclass clazz = FindClassOrDie(env, CLASS_PATH);
 
-    gFields.contextID =
-        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+    gProxyOffsets.proxy_class = MakeGlobalRefOrDie(env, clazz);
+    gProxyOffsets.contextID =
+        GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
+    gProxyOffsets.constructID = GetMethodIDOrDie(env, clazz, "<init>", "()V");
+    gProxyOffsets.sendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
+            "(Landroid/os/IHwBinder$DeathRecipient;J)V");
 
-    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
+    clazz = FindClassOrDie(env, "java/lang/Class");
+    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
 }
 
 // static
 sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
         JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) {
     sp<JHwRemoteBinder> old =
-        (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+        (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
 
     if (context != NULL) {
         context->incStrong(NULL /* id */);
@@ -67,7 +238,7 @@
         old->decStrong(NULL /* id */);
     }
 
-    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+    env->SetLongField(thiz, gProxyOffsets.contextID, (long)context.get());
 
     return old;
 }
@@ -75,7 +246,7 @@
 // static
 sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext(
         JNIEnv *env, jobject thiz) {
-    return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+    return (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
 }
 
 // static
@@ -84,7 +255,7 @@
     ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
 
     // XXX Have to look up the constructor here because otherwise that static
-    // class initializer isn't called and gFields.constructID is undefined :(
+    // class initializer isn't called and gProxyOffsets.constructID is undefined :(
 
     jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
 
@@ -97,6 +268,7 @@
 JHwRemoteBinder::JHwRemoteBinder(
         JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
     : mBinder(binder) {
+    mDeathRecipientList = new HwBinderDeathRecipientList();
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
@@ -114,7 +286,7 @@
     mClass = NULL;
 }
 
-sp<hardware::IBinder> JHwRemoteBinder::getBinder() {
+sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
     return mBinder;
 }
 
@@ -122,6 +294,10 @@
     mBinder = binder;
 }
 
+sp<HwBinderDeathRecipientList> JHwRemoteBinder::getDeathRecipientList() const {
+    return mDeathRecipientList;
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -171,7 +347,74 @@
         JHwParcel::GetNativeContext(env, replyObj)->getParcel();
 
     status_t err = binder->transact(code, *request, reply, flags);
-    signalExceptionForError(env, err);
+    signalExceptionForError(env, err, true /* canThrowRemoteException */);
+}
+
+static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
+        jobject recipient, jlong cookie)
+{
+    if (recipient == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return JNI_FALSE;
+    }
+
+    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
+    sp<hardware::IBinder> binder = context->getBinder();
+
+    if (!binder->localBinder()) {
+        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
+        sp<HwBinderDeathRecipient> jdr = new HwBinderDeathRecipient(env, recipient, cookie, list);
+        status_t err = binder->linkToDeath(jdr, NULL, 0);
+        if (err != NO_ERROR) {
+            // Failure adding the death recipient, so clear its reference
+            // now.
+            jdr->clearReference();
+            return JNI_FALSE;
+        }
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
+                                                 jobject recipient)
+{
+    jboolean res = JNI_FALSE;
+    if (recipient == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return res;
+    }
+
+    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
+    sp<hardware::IBinder> binder = context->getBinder();
+
+    if (!binder->localBinder()) {
+        status_t err = NAME_NOT_FOUND;
+
+        // If we find the matching recipient, proceed to unlink using that
+        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
+        sp<HwBinderDeathRecipient> origJDR = list->find(recipient);
+        if (origJDR != NULL) {
+            wp<hardware::IBinder::DeathRecipient> dr;
+            err = binder->unlinkToDeath(origJDR, NULL, 0, &dr);
+            if (err == NO_ERROR && dr != NULL) {
+                sp<hardware::IBinder::DeathRecipient> sdr = dr.promote();
+                HwBinderDeathRecipient* jdr = static_cast<HwBinderDeathRecipient*>(sdr.get());
+                if (jdr != NULL) {
+                    jdr->clearReference();
+                }
+            }
+        }
+
+        if (err == NO_ERROR || err == DEAD_OBJECT) {
+            res = JNI_TRUE;
+        } else {
+            jniThrowException(env, "java/util/NoSuchElementException",
+                              "Death link does not exist");
+        }
+    }
+
+    return res;
 }
 
 static JNINativeMethod gMethods[] = {
@@ -183,6 +426,14 @@
     { "transact",
         "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
         (void *)JHwRemoteBinder_native_transact },
+
+    {"linkToDeath",
+        "(Landroid/os/IHwBinder$DeathRecipient;J)Z",
+        (void*)JHwRemoteBinder_linkToDeath},
+
+    {"unlinkToDeath",
+        "(Landroid/os/IHwBinder$DeathRecipient;)Z",
+        (void*)JHwRemoteBinder_unlinkToDeath},
 };
 
 namespace android {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index fd33338..77a0278 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -20,10 +20,33 @@
 #include <android-base/macros.h>
 #include <hwbinder/Binder.h>
 #include <jni.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
+// Per-IBinder death recipient bookkeeping.  This is how we reconcile local jobject
+// death recipient references passed in through JNI with the permanent corresponding
+// HwBinderDeathRecipient objects.
+
+class HwBinderDeathRecipient;
+
+class HwBinderDeathRecipientList : public RefBase {
+    List< sp<HwBinderDeathRecipient> > mList;
+    Mutex mLock;
+
+public:
+    HwBinderDeathRecipientList();
+    ~HwBinderDeathRecipientList();
+
+    void add(const sp<HwBinderDeathRecipient>& recipient);
+    void remove(const sp<HwBinderDeathRecipient>& recipient);
+    sp<HwBinderDeathRecipient> find(jobject recipient);
+
+    Mutex& lock();  // Use with care; specifically for mutual exclusion during binder death
+};
+
 struct JHwRemoteBinder : public RefBase {
     static void InitClass(JNIEnv *env);
 
@@ -37,8 +60,9 @@
     JHwRemoteBinder(
             JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder);
 
-    sp<hardware::IBinder> getBinder();
+    sp<hardware::IBinder> getBinder() const;
     void setBinder(const sp<hardware::IBinder> &binder);
+    sp<HwBinderDeathRecipientList> getDeathRecipientList() const;
 
 protected:
     virtual ~JHwRemoteBinder();
@@ -48,7 +72,7 @@
     jobject mObject;
 
     sp<hardware::IBinder> mBinder;
-
+    sp<HwBinderDeathRecipientList> mDeathRecipientList;
     DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
 };
 
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 4a2b881..9fa90ac 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -43,7 +43,6 @@
     jfieldID uid;
     jfieldID set;
     jfieldID tag;
-    jfieldID metered;
     jfieldID roaming;
     jfieldID rxBytes;
     jfieldID rxPackets;
@@ -240,9 +239,6 @@
     ScopedIntArrayRW tag(env, get_int_array(env, stats,
             gNetworkStatsClassInfo.tag, size, grow));
     if (tag.get() == NULL) return -1;
-    ScopedIntArrayRW metered(env, get_int_array(env, stats,
-            gNetworkStatsClassInfo.metered, size, grow));
-    if (metered.get() == NULL) return -1;
     ScopedIntArrayRW roaming(env, get_int_array(env, stats,
             gNetworkStatsClassInfo.roaming, size, grow));
     if (roaming.get() == NULL) return -1;
@@ -269,7 +265,7 @@
         uid[i] = lines[i].uid;
         set[i] = lines[i].set;
         tag[i] = lines[i].tag;
-        // Metered and Roaming are populated in Java-land by inspecting the iface properties.
+        // Roaming is populated in Java-land by inspecting the iface properties.
         rxBytes[i] = lines[i].rxBytes;
         rxPackets[i] = lines[i].rxPackets;
         txBytes[i] = lines[i].txBytes;
@@ -283,7 +279,6 @@
         env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
-        env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
         env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
@@ -316,7 +311,6 @@
     gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I");
     gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I");
     gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
-    gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
     gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
     gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
     gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index fec8f4e..94935cc 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -56,7 +56,7 @@
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
-#include "fd_utils-inl.h"
+#include "fd_utils.h"
 
 #include "nativebridge/native_bridge.h"
 
@@ -684,6 +684,16 @@
   return pid;
 }
 
+static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
+        JNIEnv* env, jclass, jstring path) {
+    ScopedUtfChars path_native(env, path);
+    const char* path_cstr = path_native.c_str();
+    if (!path_cstr) {
+        RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+    }
+    FileDescriptorWhitelist::Get()->Allow(path_cstr);
+}
+
 static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
     // Zygote process unmount root storage space initially before every child processes are forked.
     // Every forked child processes (include SystemServer) only mount their own root storage space
@@ -728,6 +738,8 @@
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+    { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
+      (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
     { "nativeUnmountStorageOnInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
 };
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
deleted file mode 100644
index b78b8ff..0000000
--- a/core/jni/fd_utils-inl.h
+++ /dev/null
@@ -1,564 +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.
- */
-
-#include <string>
-#include <unordered_map>
-#include <set>
-#include <vector>
-#include <algorithm>
-
-#include <android-base/strings.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cutils/log.h>
-#include "JNIHelp.h"
-#include "ScopedPrimitiveArray.h"
-
-// Whitelist of open paths that the zygote is allowed to keep open.
-//
-// In addition to the paths listed here, all files ending with
-// ".jar" under /system/framework" are whitelisted. See
-// FileDescriptorInfo::IsWhitelisted for the canonical definition.
-//
-// If the whitelisted path is associated with a regular file or a
-// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted  path is associated with a
-// AF_UNIX socket, the socket will refer to /dev/null after each
-// fork, and all operations on it will fail.
-static const char* kPathWhitelist[] = {
-  "/dev/null",
-  "/dev/socket/zygote",
-  "/dev/socket/zygote_secondary",
-  "/dev/socket/webview_zygote",
-  "/sys/kernel/debug/tracing/trace_marker",
-  "/system/framework/framework-res.apk",
-  "/dev/urandom",
-  "/dev/ion",
-  "/dev/dri/renderD129", // Fixes b/31172436
-};
-
-static const char* kFdPath = "/proc/self/fd";
-
-// Keeps track of all relevant information (flags, offset etc.) of an
-// open zygote file descriptor.
-class FileDescriptorInfo {
- public:
-  // Create a FileDescriptorInfo for a given file descriptor. Returns
-  // |NULL| if an error occurred.
-  static FileDescriptorInfo* createFromFd(int fd) {
-    struct stat f_stat;
-    // This should never happen; the zygote should always have the right set
-    // of permissions required to stat all its open files.
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    if (S_ISSOCK(f_stat.st_mode)) {
-      std::string socket_name;
-      if (!GetSocketName(fd, &socket_name)) {
-        return NULL;
-      }
-
-      if (!IsWhitelisted(socket_name)) {
-        ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
-        return NULL;
-      }
-
-      return new FileDescriptorInfo(fd);
-    }
-
-    // We only handle whitelisted regular files and character devices. Whitelisted
-    // character devices must provide a guarantee of sensible behaviour when
-    // reopened.
-    //
-    // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
-    // S_ISLINK : Not supported.
-    // S_ISBLK : Not supported.
-    // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
-    // with the child process across forks but those should have been closed
-    // before we got to this point.
-    if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
-      ALOGE("Unsupported st_mode %d", f_stat.st_mode);
-      return NULL;
-    }
-
-    std::string file_path;
-    if (!Readlink(fd, &file_path)) {
-      return NULL;
-    }
-
-    if (!IsWhitelisted(file_path)) {
-      ALOGE("Not whitelisted : %s", file_path.c_str());
-      return NULL;
-    }
-
-    // File descriptor flags : currently on FD_CLOEXEC. We can set these
-    // using F_SETFD - we're single threaded at this point of execution so
-    // there won't be any races.
-    const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
-    if (fd_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File status flags :
-    // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
-    //   to the open() call.
-    //
-    // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
-    //   do about these, since the file has already been created. We shall ignore
-    //   them here.
-    //
-    // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
-    //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
-    //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
-    //   their presence and pass them in to open().
-    int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
-    if (fs_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File offset : Ignore the offset for non seekable files.
-    const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
-
-    // We pass the flags that open accepts to open, and use F_SETFL for
-    // the rest of them.
-    static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
-    int open_flags = fs_flags & (kOpenFlags);
-    fs_flags = fs_flags & (~(kOpenFlags));
-
-    return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
-  }
-
-  // Checks whether the file descriptor associated with this object
-  // refers to the same description.
-  bool Restat() const {
-    struct stat f_stat;
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      return false;
-    }
-
-    return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
-  }
-
-  bool ReopenOrDetach() const {
-    if (is_sock) {
-      return DetachSocket();
-    }
-
-    // NOTE: This might happen if the file was unlinked after being opened.
-    // It's a common pattern in the case of temporary files and the like but
-    // we should not allow such usage from the zygote.
-    const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
-
-    if (new_fd == -1) {
-      ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
-      return false;
-    }
-
-    if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
-      close(new_fd);
-      ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
-      close(new_fd);
-      ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
-      return false;
-    }
-
-    close(new_fd);
-
-    return true;
-  }
-
-  const int fd;
-  const struct stat stat;
-  const std::string file_path;
-  const int open_flags;
-  const int fd_flags;
-  const int fs_flags;
-  const off_t offset;
-  const bool is_sock;
-
- private:
-  FileDescriptorInfo(int fd) :
-    fd(fd),
-    stat(),
-    open_flags(0),
-    fd_flags(0),
-    fs_flags(0),
-    offset(0),
-    is_sock(true) {
-  }
-
-  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
-                     int fd_flags, int fs_flags, off_t offset) :
-    fd(fd),
-    stat(stat),
-    file_path(file_path),
-    open_flags(open_flags),
-    fd_flags(fd_flags),
-    fs_flags(fs_flags),
-    offset(offset),
-    is_sock(false) {
-  }
-
-  // Returns true iff. a given path is whitelisted. A path is whitelisted
-  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar" or if it is a system
-  // framework overlay.
-  static bool IsWhitelisted(const std::string& path) {
-    for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
-      if (kPathWhitelist[i] == path) {
-        return true;
-      }
-    }
-
-    static const char* kFrameworksPrefix = "/system/framework/";
-    static const char* kJarSuffix = ".jar";
-    if (android::base::StartsWith(path, kFrameworksPrefix)
-        && android::base::EndsWith(path, kJarSuffix)) {
-      return true;
-    }
-
-    // Whitelist files needed for Runtime Resource Overlay, like these:
-    // /system/vendor/overlay/framework-res.apk
-    // /system/vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
-    // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-    // /data/resource-cache/system@vendor@overlay@PG@framework-res.apk@idmap
-    static const char* kOverlayDir = "/system/vendor/overlay/";
-    static const char* kApkSuffix = ".apk";
-
-    if (android::base::StartsWith(path, kOverlayDir)
-        && android::base::EndsWith(path, kApkSuffix)
-        && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
-    static const char* kOverlayIdmapSuffix = ".apk@idmap";
-    if (android::base::StartsWith(path, kOverlayIdmapPrefix)
-        && android::base::EndsWith(path, kOverlayIdmapSuffix)) {
-      return true;
-    }
-
-    return false;
-  }
-
-  // TODO: Call android::base::Readlink instead of copying the code here.
-  static bool Readlink(const int fd, std::string* result) {
-    char path[64];
-    snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
-
-    // Code copied from android::base::Readlink starts here :
-
-    // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
-    // and truncates to whatever size you do supply, so it can't be used to query.
-    // We could call lstat first, but that would introduce a race condition that
-    // we couldn't detect.
-    // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
-    char buf[4096];
-    ssize_t len = readlink(path, buf, sizeof(buf));
-    if (len == -1) return false;
-
-    result->assign(buf, len);
-    return true;
-  }
-
-  // Returns the locally-bound name of the socket |fd|. Returns true
-  // iff. all of the following hold :
-  //
-  // - the socket's sa_family is AF_UNIX.
-  // - the length of the path is greater than zero (i.e, not an unnamed socket).
-  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
-  //   address).
-  static bool GetSocketName(const int fd, std::string* result) {
-    sockaddr_storage ss;
-    sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
-    socklen_t addr_len = sizeof(ss);
-
-    if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
-      ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (addr->sa_family != AF_UNIX) {
-      ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
-      return false;
-    }
-
-    const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
-
-    size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
-    // This is an unnamed local socket, we do not accept it.
-    if (path_len == 0) {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
-      return false;
-    }
-
-    // This is a local socket with an abstract address, we do not accept it.
-    if (unix_addr->sun_path[0] == '\0') {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
-      return false;
-    }
-
-    // If we're here, sun_path must refer to a null terminated filesystem
-    // pathname (man 7 unix). Remove the terminator before assigning it to an
-    // std::string.
-    if (unix_addr->sun_path[path_len - 1] ==  '\0') {
-      --path_len;
-    }
-
-    result->assign(unix_addr->sun_path, path_len);
-    return true;
-  }
-
-  bool DetachSocket() const {
-    const int dev_null_fd = open("/dev/null", O_RDWR);
-    if (dev_null_fd < 0) {
-      ALOGE("Failed to open /dev/null : %s", strerror(errno));
-      return false;
-    }
-
-    if (dup2(dev_null_fd, fd) == -1) {
-      ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (close(dev_null_fd) == -1) {
-      ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
-      return false;
-    }
-
-    return true;
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
-};
-
-// A FileDescriptorTable is a collection of FileDescriptorInfo objects
-// keyed by their FDs.
-class FileDescriptorTable {
- public:
-  // Creates a new FileDescriptorTable. This function scans
-  // /proc/self/fd for the list of open file descriptors and collects
-  // information about them. Returns NULL if an error occurs.
-  static FileDescriptorTable* Create() {
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return NULL;
-    }
-    int dir_fd = dirfd(d);
-    dirent* e;
-
-    std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-      if (info == NULL) {
-        if (closedir(d) == -1) {
-          ALOGE("Unable to close directory : %s", strerror(errno));
-        }
-        return NULL;
-      }
-      open_fd_map[fd] = info;
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return NULL;
-    }
-    return new FileDescriptorTable(open_fd_map);
-  }
-
-  bool Restat() {
-    std::set<int> open_fds;
-
-    // First get the list of open descriptors.
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return false;
-    }
-
-    int dir_fd = dirfd(d);
-    dirent* e;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      open_fds.insert(fd);
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return false;
-    }
-
-    return RestatInternal(open_fds);
-  }
-
-  // Reopens all file descriptors that are contained in the table. Returns true
-  // if all descriptors were successfully re-opened or detached, and false if an
-  // error occurred.
-  bool ReopenOrDetach() {
-    std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
-    for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
-      const FileDescriptorInfo* info = it->second;
-      if (info == NULL || !info->ReopenOrDetach()) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
- private:
-  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
-      : open_fd_map_(map) {
-  }
-
-  bool RestatInternal(std::set<int>& open_fds) {
-    bool error = false;
-
-    // Iterate through the list of file descriptors we've already recorded
-    // and check whether :
-    //
-    // (a) they continue to be open.
-    // (b) they refer to the same file.
-    std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
-    while (it != open_fd_map_.end()) {
-      std::set<int>::const_iterator element = open_fds.find(it->first);
-      if (element == open_fds.end()) {
-        // The entry from the file descriptor table is no longer in the list
-        // of open files. We warn about this condition and remove it from
-        // the list of FDs under consideration.
-        //
-        // TODO(narayan): This will be an error in a future android release.
-        // error = true;
-        // ALOGW("Zygote closed file descriptor %d.", it->first);
-        it = open_fd_map_.erase(it);
-      } else {
-        // The entry from the file descriptor table is still open. Restat
-        // it and check whether it refers to the same file.
-        const bool same_file = it->second->Restat();
-        if (!same_file) {
-          // The file descriptor refers to a different description. We must
-          // update our entry in the table.
-          delete it->second;
-          it->second = FileDescriptorInfo::createFromFd(*element);
-          if (it->second == NULL) {
-            // The descriptor no longer no longer refers to a whitelisted file.
-            // We flag an error and remove it from the list of files we're
-            // tracking.
-            error = true;
-            it = open_fd_map_.erase(it);
-          } else {
-            // Successfully restatted the file, move on to the next open FD.
-            ++it;
-          }
-        } else {
-          // It's the same file. Nothing to do here. Move on to the next open
-          // FD.
-          ++it;
-        }
-
-        // Finally, remove the FD from the set of open_fds. We do this last because
-        // |element| will not remain valid after a call to erase.
-        open_fds.erase(element);
-      }
-    }
-
-    if (open_fds.size() > 0) {
-      // The zygote has opened new file descriptors since our last inspection.
-      // We warn about this condition and add them to our table.
-      //
-      // TODO(narayan): This will be an error in a future android release.
-      // error = true;
-      // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
-
-      // TODO(narayan): This code will be removed in a future android release.
-      std::set<int>::const_iterator it;
-      for (it = open_fds.begin(); it != open_fds.end(); ++it) {
-        const int fd = (*it);
-        FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-        if (info == NULL) {
-          // A newly opened file is not on the whitelist. Flag an error and
-          // continue.
-          error = true;
-        } else {
-          // Track the newly opened file.
-          open_fd_map_[fd] = info;
-        }
-      }
-    }
-
-    return !error;
-  }
-
-  static int ParseFd(dirent* e, int dir_fd) {
-    char* end;
-    const int fd = strtol(e->d_name, &end, 10);
-    if ((*end) != '\0') {
-      return -1;
-    }
-
-    // Don't bother with the standard input/output/error, they're handled
-    // specially post-fork anyway.
-    if (fd <= STDERR_FILENO || fd == dir_fd) {
-      return -1;
-    }
-
-    return fd;
-  }
-
-  // Invariant: All values in this unordered_map are non-NULL.
-  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
-};
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
new file mode 100644
index 0000000..969d336f3
--- /dev/null
+++ b/core/jni/fd_utils.cpp
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ */
+
+#include "fd_utils.h"
+
+#include <algorithm>
+
+#include <fcntl.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+// Static whitelist of open paths that the zygote is allowed to keep open.
+static const char* kPathWhitelist[] = {
+  "/dev/null",
+  "/dev/socket/zygote",
+  "/dev/socket/zygote_secondary",
+  "/dev/socket/webview_zygote",
+  "/sys/kernel/debug/tracing/trace_marker",
+  "/system/framework/framework-res.apk",
+  "/dev/urandom",
+  "/dev/ion",
+  "/dev/dri/renderD129", // Fixes b/31172436
+};
+
+static const char kFdPath[] = "/proc/self/fd";
+
+// static
+FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
+  if (instance_ == nullptr) {
+    instance_ = new FileDescriptorWhitelist();
+  }
+  return instance_;
+}
+
+bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
+  // Check the static whitelist path.
+  for (const auto& whitelist_path : kPathWhitelist) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  // Check any paths added to the dynamic whitelist.
+  for (const auto& whitelist_path : whitelist_) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  static const std::string kFrameworksPrefix = "/system/framework/";
+  static const std::string kJarSuffix = ".jar";
+  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+    return true;
+  }
+
+  // Whitelist files needed for Runtime Resource Overlay, like these:
+  // /system/vendor/overlay/framework-res.apk
+  // /system/vendor/overlay-subdir/pg/framework-res.apk
+  // /vendor/overlay/framework-res.apk
+  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+  // See AssetManager.cpp for more details on overlay-subdir.
+  static const std::string kOverlayDir = "/system/vendor/overlay/";
+  static const std::string kVendorOverlayDir = "/vendor/overlay";
+  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const std::string kApkSuffix = ".apk";
+
+  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
+       || StartsWith(path, kVendorOverlayDir))
+      && EndsWith(path, kApkSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
+  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
+  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  // All regular files that are placed under this path are whitelisted automatically.
+  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  return false;
+}
+
+FileDescriptorWhitelist::FileDescriptorWhitelist()
+    : whitelist_() {
+}
+
+// TODO: Call android::base::StartsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::StartsWith(const std::string& str,
+                                         const std::string& prefix) {
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// TODO: Call android::base::EndsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::EndsWith(const std::string& str,
+                                       const std::string& suffix) {
+  if (suffix.size() > str.size()) {
+    return false;
+  }
+
+  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+
+// static
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
+  struct stat f_stat;
+  // This should never happen; the zygote should always have the right set
+  // of permissions required to stat all its open files.
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+
+  if (S_ISSOCK(f_stat.st_mode)) {
+    std::string socket_name;
+    if (!GetSocketName(fd, &socket_name)) {
+      return NULL;
+    }
+
+    if (!whitelist->IsAllowed(socket_name)) {
+      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+      return NULL;
+    }
+
+    return new FileDescriptorInfo(fd);
+  }
+
+  // We only handle whitelisted regular files and character devices. Whitelisted
+  // character devices must provide a guarantee of sensible behaviour when
+  // reopened.
+  //
+  // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
+  // S_ISLINK : Not supported.
+  // S_ISBLK : Not supported.
+  // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
+  // with the child process across forks but those should have been closed
+  // before we got to this point.
+  if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
+    ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+    return NULL;
+  }
+
+  std::string file_path;
+  if (!Readlink(fd, &file_path)) {
+    return NULL;
+  }
+
+  if (!whitelist->IsAllowed(file_path)) {
+    ALOGE("Not whitelisted : %s", file_path.c_str());
+    return NULL;
+  }
+
+  // File descriptor flags : currently on FD_CLOEXEC. We can set these
+  // using F_SETFD - we're single threaded at this point of execution so
+  // there won't be any races.
+  const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
+  if (fd_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File status flags :
+  // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
+  //   to the open() call.
+  //
+  // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
+  //   do about these, since the file has already been created. We shall ignore
+  //   them here.
+  //
+  // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
+  //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
+  //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
+  //   their presence and pass them in to open().
+  int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+  if (fs_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File offset : Ignore the offset for non seekable files.
+  const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+
+  // We pass the flags that open accepts to open, and use F_SETFL for
+  // the rest of them.
+  static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
+  int open_flags = fs_flags & (kOpenFlags);
+  fs_flags = fs_flags & (~(kOpenFlags));
+
+  return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
+}
+
+bool FileDescriptorInfo::Restat() const {
+  struct stat f_stat;
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    return false;
+  }
+
+  return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
+}
+
+bool FileDescriptorInfo::ReopenOrDetach() const {
+  if (is_sock) {
+    return DetachSocket();
+  }
+
+  // NOTE: This might happen if the file was unlinked after being opened.
+  // It's a common pattern in the case of temporary files and the like but
+  // we should not allow such usage from the zygote.
+  const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
+
+  if (new_fd == -1) {
+    ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+    return false;
+  }
+
+  if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
+    close(new_fd);
+    ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+    close(new_fd);
+    ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+    return false;
+  }
+
+  close(new_fd);
+
+  return true;
+}
+
+FileDescriptorInfo::FileDescriptorInfo(int fd) :
+  fd(fd),
+  stat(),
+  open_flags(0),
+  fd_flags(0),
+  fs_flags(0),
+  offset(0),
+  is_sock(true) {
+}
+
+FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file_path,
+                                       int fd, int open_flags, int fd_flags, int fs_flags,
+                                       off_t offset) :
+  fd(fd),
+  stat(stat),
+  file_path(file_path),
+  open_flags(open_flags),
+  fd_flags(fd_flags),
+  fs_flags(fs_flags),
+  offset(offset),
+  is_sock(false) {
+}
+
+// TODO: Call android::base::Readlink instead of copying the code here.
+// static
+bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+  // Code copied from android::base::Readlink starts here :
+
+  // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
+  // and truncates to whatever size you do supply, so it can't be used to query.
+  // We could call lstat first, but that would introduce a race condition that
+  // we couldn't detect.
+  // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
+  char buf[4096];
+  ssize_t len = readlink(path, buf, sizeof(buf));
+  if (len == -1) return false;
+
+  result->assign(buf, len);
+  return true;
+}
+
+// static
+bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
+  sockaddr_storage ss;
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
+  socklen_t addr_len = sizeof(ss);
+
+  if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
+    ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (addr->sa_family != AF_UNIX) {
+    ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+    return false;
+  }
+
+  const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
+
+  size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
+  // This is an unnamed local socket, we do not accept it.
+  if (path_len == 0) {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+    return false;
+  }
+
+  // This is a local socket with an abstract address, we do not accept it.
+  if (unix_addr->sun_path[0] == '\0') {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+    return false;
+  }
+
+  // If we're here, sun_path must refer to a null terminated filesystem
+  // pathname (man 7 unix). Remove the terminator before assigning it to an
+  // std::string.
+  if (unix_addr->sun_path[path_len - 1] ==  '\0') {
+    --path_len;
+  }
+
+  result->assign(unix_addr->sun_path, path_len);
+  return true;
+}
+
+bool FileDescriptorInfo::DetachSocket() const {
+  const int dev_null_fd = open("/dev/null", O_RDWR);
+  if (dev_null_fd < 0) {
+    ALOGE("Failed to open /dev/null : %s", strerror(errno));
+    return false;
+  }
+
+  if (dup2(dev_null_fd, fd) == -1) {
+    ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (close(dev_null_fd) == -1) {
+    ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+    return false;
+  }
+
+  return true;
+}
+
+// static
+FileDescriptorTable* FileDescriptorTable::Create() {
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return NULL;
+  }
+  int dir_fd = dirfd(d);
+  dirent* e;
+
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+    if (info == NULL) {
+      if (closedir(d) == -1) {
+        ALOGE("Unable to close directory : %s", strerror(errno));
+      }
+      return NULL;
+    }
+    open_fd_map[fd] = info;
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return NULL;
+  }
+  return new FileDescriptorTable(open_fd_map);
+}
+
+bool FileDescriptorTable::Restat() {
+  std::set<int> open_fds;
+
+  // First get the list of open descriptors.
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return false;
+  }
+
+  int dir_fd = dirfd(d);
+  dirent* e;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    open_fds.insert(fd);
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return false;
+  }
+
+  return RestatInternal(open_fds);
+}
+
+// Reopens all file descriptors that are contained in the table. Returns true
+// if all descriptors were successfully re-opened or detached, and false if an
+// error occurred.
+bool FileDescriptorTable::ReopenOrDetach() {
+  std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
+  for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+    const FileDescriptorInfo* info = it->second;
+    if (info == NULL || !info->ReopenOrDetach()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+FileDescriptorTable::FileDescriptorTable(
+    const std::unordered_map<int, FileDescriptorInfo*>& map)
+    : open_fd_map_(map) {
+}
+
+bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
+  bool error = false;
+
+  // Iterate through the list of file descriptors we've already recorded
+  // and check whether :
+  //
+  // (a) they continue to be open.
+  // (b) they refer to the same file.
+  std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
+  while (it != open_fd_map_.end()) {
+    std::set<int>::const_iterator element = open_fds.find(it->first);
+    if (element == open_fds.end()) {
+      // The entry from the file descriptor table is no longer in the list
+      // of open files. We warn about this condition and remove it from
+      // the list of FDs under consideration.
+      //
+      // TODO(narayan): This will be an error in a future android release.
+      // error = true;
+      // ALOGW("Zygote closed file descriptor %d.", it->first);
+      it = open_fd_map_.erase(it);
+    } else {
+      // The entry from the file descriptor table is still open. Restat
+      // it and check whether it refers to the same file.
+      const bool same_file = it->second->Restat();
+      if (!same_file) {
+        // The file descriptor refers to a different description. We must
+        // update our entry in the table.
+        delete it->second;
+        it->second = FileDescriptorInfo::CreateFromFd(*element);
+        if (it->second == NULL) {
+          // The descriptor no longer no longer refers to a whitelisted file.
+          // We flag an error and remove it from the list of files we're
+          // tracking.
+          error = true;
+          it = open_fd_map_.erase(it);
+        } else {
+          // Successfully restatted the file, move on to the next open FD.
+          ++it;
+        }
+      } else {
+        // It's the same file. Nothing to do here. Move on to the next open
+        // FD.
+        ++it;
+      }
+
+      // Finally, remove the FD from the set of open_fds. We do this last because
+      // |element| will not remain valid after a call to erase.
+      open_fds.erase(element);
+    }
+  }
+
+  if (open_fds.size() > 0) {
+    // The zygote has opened new file descriptors since our last inspection.
+    // We warn about this condition and add them to our table.
+    //
+    // TODO(narayan): This will be an error in a future android release.
+    // error = true;
+    // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
+
+    // TODO(narayan): This code will be removed in a future android release.
+    std::set<int>::const_iterator it;
+    for (it = open_fds.begin(); it != open_fds.end(); ++it) {
+      const int fd = (*it);
+      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+      if (info == NULL) {
+        // A newly opened file is not on the whitelist. Flag an error and
+        // continue.
+        error = true;
+      } else {
+        // Track the newly opened file.
+        open_fd_map_[fd] = info;
+      }
+    }
+  }
+
+  return !error;
+}
+
+// static
+int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+  char* end;
+  const int fd = strtol(e->d_name, &end, 10);
+  if ((*end) != '\0') {
+    return -1;
+  }
+
+  // Don't bother with the standard input/output/error, they're handled
+  // specially post-fork anyway.
+  if (fd <= STDERR_FILENO || fd == dir_fd) {
+    return -1;
+  }
+
+  return fd;
+}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
new file mode 100644
index 0000000..9e3afd9
--- /dev/null
+++ b/core/jni/fd_utils.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+#define FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// paths dynamically added with Allow(), all files ending with ".jar"
+// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted  path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+class FileDescriptorWhitelist {
+ public:
+  // Lazily creates the global whitelist.
+  static FileDescriptorWhitelist* Get();
+
+  // Adds a path to the whitelist.
+  void Allow(const std::string& path) {
+    whitelist_.push_back(path);
+  }
+
+  // Returns true iff. a given path is whitelisted. A path is whitelisted
+  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+  // under /system/framework that ends with ".jar" or if it is a system
+  // framework overlay.
+  bool IsAllowed(const std::string& path) const;
+
+ private:
+  FileDescriptorWhitelist();
+
+  static bool StartsWith(const std::string& str, const std::string& prefix);
+
+  static bool EndsWith(const std::string& str, const std::string& suffix);
+
+  static FileDescriptorWhitelist* instance_;
+
+  std::vector<std::string> whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+};
+
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+  // Create a FileDescriptorInfo for a given file descriptor. Returns
+  // |NULL| if an error occurred.
+  static FileDescriptorInfo* CreateFromFd(int fd);
+
+  // Checks whether the file descriptor associated with this object
+  // refers to the same description.
+  bool Restat() const;
+
+  bool ReopenOrDetach() const;
+
+  const int fd;
+  const struct stat stat;
+  const std::string file_path;
+  const int open_flags;
+  const int fd_flags;
+  const int fs_flags;
+  const off_t offset;
+  const bool is_sock;
+
+ private:
+  FileDescriptorInfo(int fd);
+
+  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+                     int fd_flags, int fs_flags, off_t offset);
+
+  static bool Readlink(const int fd, std::string* result);
+
+  // Returns the locally-bound name of the socket |fd|. Returns true
+  // iff. all of the following hold :
+  //
+  // - the socket's sa_family is AF_UNIX.
+  // - the length of the path is greater than zero (i.e, not an unnamed socket).
+  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+  //   address).
+  static bool GetSocketName(const int fd, std::string* result);
+
+  bool DetachSocket() const;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
+// A FileDescriptorTable is a collection of FileDescriptorInfo objects
+// keyed by their FDs.
+class FileDescriptorTable {
+ public:
+  // Creates a new FileDescriptorTable. This function scans
+  // /proc/self/fd for the list of open file descriptors and collects
+  // information about them. Returns NULL if an error occurs.
+  static FileDescriptorTable* Create();
+
+  bool Restat();
+
+  // Reopens all file descriptors that are contained in the table. Returns true
+  // if all descriptors were successfully re-opened or detached, and false if an
+  // error occurred.
+  bool ReopenOrDetach();
+
+ private:
+  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+
+  bool RestatInternal(std::set<int>& open_fds);
+
+  static int ParseFd(dirent* e, int dir_fd);
+
+  // Invariant: All values in this unordered_map are non-NULL.
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
+};
+
+#endif  // FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e98e34..048214a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -167,6 +167,8 @@
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
@@ -181,6 +183,8 @@
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
     <protected-broadcast
+        android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
@@ -1228,6 +1232,12 @@
     <permission android:name="android.permission.SCORE_NETWORKS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows applications to request network
+         recommendations and scores from the NetworkScoreService.
+         <p>Not for use by third-party applications. @hide -->
+    <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
+        android:protectionLevel="signature" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
@@ -2044,6 +2054,12 @@
     <permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
         android:protectionLevel="signature" />
 
+    <!-- Allows the system to bind to the discovered Network Recommendation Service.
+         @SystemApi @hide -->
+    <permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
+        android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
+
     <!-- ========================================= -->
     <!-- Permissions for special development tools -->
     <!-- ========================================= -->
@@ -2632,7 +2648,10 @@
         android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows an application to broadcast privileged networking requests.
-         <p>Not for use by third-party applications. @hide -->
+         <p>Not for use by third-party applications.
+         @hide
+         @deprecated Use {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} instead
+    -->
     <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
         android:protectionLevel="signature|privileged" />
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4cf1226..42cb55b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1280,9 +1280,8 @@
     <!-- A list of potential packages, in priority order, that may contain a
          network recommendation provider. A network recommendation provider must:
              * Be granted the SCORE_NETWORKS permission.
-             * Include a Receiver for the android.net.scoring.SCORE_NETWORKS action guarded by the
-               BROADCAST_NETWORK_PRIVILEGED permission.
-             * Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action.
+             * Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action
+               protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission.
 
          This may be empty if network scoring and recommending isn't supported.
          -->
@@ -1682,7 +1681,7 @@
     <bool name="config_actionMenuItemAllCaps">true</bool>
 
     <!-- Remote server that can provide NTP responses. -->
-    <string translatable="false" name="config_ntpServer">time.android.com</string>
+    <string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>
     <!-- Normal polling frequency in milliseconds -->
     <integer name="config_ntpPollingInterval">86400000</integer>
     <!-- Try-again polling interval in milliseconds, in case the network request failed -->
@@ -2676,4 +2675,7 @@
 
     <!-- An array of packages for which notifications cannot be blocked. -->
     <string-array translatable="false" name="config_nonBlockableNotificationPackages" />
+
+    <!-- Component name of the default cell broadcast receiver -->
+    <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fe88cd1..3aeda58 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2725,6 +2725,7 @@
 
   <java-symbol type="bool" name="config_permissionReviewRequired" />
 
+
   <java-symbol type="drawable" name="ic_restart" />
 
   <java-symbol type="drawable" name="emergency_icon" />
@@ -2739,4 +2740,6 @@
 
 <!-- Network Recommendation -->
   <java-symbol type="array" name="config_networkRecommendationPackageNames" />
+
+  <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" />
 </resources>
diff --git a/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
index 4f2387d..fcc6389 100644
--- a/core/tests/coretests/src/android/net/IpPrefixTest.java
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -18,14 +18,14 @@
 
 import android.net.IpPrefix;
 import android.os.Parcel;
+import static android.test.MoreAsserts.assertNotEqual;
 import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.assertArrayEquals;
 import java.net.InetAddress;
 import java.util.Random;
 import junit.framework.TestCase;
 
-import static android.test.MoreAsserts.assertNotEqual;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
 
 public class IpPrefixTest extends TestCase {
 
@@ -242,42 +242,25 @@
 
     @SmallTest
     public void testHashCode() {
-        IpPrefix p = new IpPrefix(new byte[4], 0);
+        IpPrefix p;
+        int oldCode = -1;
         Random random = new Random();
         for (int i = 0; i < 100; i++) {
-            final IpPrefix oldP = p;
             if (random.nextBoolean()) {
                 // IPv4.
                 byte[] b = new byte[4];
                 random.nextBytes(b);
                 p = new IpPrefix(b, random.nextInt(33));
+                assertNotEqual(oldCode, p.hashCode());
+                oldCode = p.hashCode();
             } else {
                 // IPv6.
                 byte[] b = new byte[16];
                 random.nextBytes(b);
                 p = new IpPrefix(b, random.nextInt(129));
+                assertNotEqual(oldCode, p.hashCode());
+                oldCode = p.hashCode();
             }
-            if (p.equals(oldP)) {
-              assertEquals(p.hashCode(), oldP.hashCode());
-            }
-            if (p.hashCode() != oldP.hashCode()) {
-              assertNotEqual(p, oldP);
-            }
-        }
-    }
-
-    @SmallTest
-    public void testHashCodeIsNotConstant() {
-        IpPrefix[] prefixes = {
-            new IpPrefix("2001:db8:f00::ace:d00d/127"),
-            new IpPrefix("192.0.2.0/23"),
-            new IpPrefix("::/0"),
-            new IpPrefix("0.0.0.0/0"),
-        };
-        for (int i = 0; i < prefixes.length; i++) {
-          for (int j = i + 1; j < prefixes.length; j++) {
-            assertNotEqual(prefixes[i].hashCode(), prefixes[j].hashCode());
-          }
         }
     }
 
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 5bfff26..29020ba 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -169,6 +169,25 @@
         assertNull(activeScorer);
     }
 
+    public void testIsCallerActiveScorer_providerNotAvailable() throws Exception {
+        ContentResolver cr = mTargetContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+        assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(924));
+    }
+
+    public void testIsCallerActiveScorer_providerAvailable() throws Exception {
+        setNetworkRecommendationPackageNames("package1");
+        mockScoreNetworksGranted("package1");
+        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+
+        ContentResolver cr = mTargetContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+
+        assertTrue(mNetworkScorerAppManager.isCallerActiveScorer(924));
+        assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(925));
+    }
+
     private void setNetworkRecommendationPackageNames(String... names) {
         if (names == null) {
             names = new String[0];
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index eb85eb4..9074f8a 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -16,9 +16,6 @@
 
 package android.net;
 
-import static android.net.NetworkStats.METERED_ALL;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
@@ -48,124 +45,103 @@
     private static final long TEST_START = 1194220800000L;
 
     public void testFindIndex() throws Exception {
-        final NetworkStats stats = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
-                        8L, 1024L, 8L, 12);
+        final NetworkStats stats = new NetworkStats(TEST_START, 4)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L, 0L,
+                        0L, 10)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 1024L,
+                        8L, 11)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                        1024L, 8L, 12)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES, 1024L, 8L,
+                        1024L, 8L, 12);
 
-        assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES));
-        assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
-        assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_NO));
-        assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
-        assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
-        assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO));
+        assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES));
+        assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+        assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+        assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+        assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, ROAMING_NO));
     }
 
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        1024L, 8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 0L, 0L,
-                        1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
-                        8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
-                        8L, 1024L, 8L, 12);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L, 0L,
+                        0L, 10)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 1024L,
+                        8L, 11)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                        1024L, 8L, 12)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                        0L, 0L, 10)
+                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 0L, 0L, 1024L,
+                        8L, 11)
+                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                        1024L, 8L, 12)
+                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES, 1024L, 8L,
+                        1024L, 8L, 12);
 
         // verify that we correctly find across regardless of hinting
         for (int hint = 0; hint < stats.size(); hint++) {
             assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
             assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
             assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
             assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
             assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
-                    METERED_NO, ROAMING_NO, hint));
-            assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
-                    METERED_YES, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
+            assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
+                    ROAMING_NO, hint));
             assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
-            assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
-                    METERED_YES, ROAMING_YES, hint));
+                    ROAMING_YES, hint));
             assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE,
-                    METERED_NO, ROAMING_NO, hint));
+                    ROAMING_NO, hint));
         }
     }
 
     public void testAddEntryGrow() throws Exception {
-        final NetworkStats stats = new NetworkStats(TEST_START, 4);
+        final NetworkStats stats = new NetworkStats(TEST_START, 3);
 
         assertEquals(0, stats.size());
-        assertEquals(4, stats.internalSize());
+        assertEquals(3, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L, 1L,
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L, 2L,
+                2L, 3);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2L, 2L, 2L,
+                2L, 4);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 3L, 3L, 2L,
+                2L, 5);
+
+        assertEquals(3, stats.size());
+        assertEquals(3, stats.internalSize());
+
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 4L, 40L, 4L,
+                40L, 7);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 5L, 50L, 4L,
+                40L, 8);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 6L, 60L, 5L,
+                50L, 10);
+        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 7L, 70L, 5L,
+                50L, 11);
+
+        assertEquals(7, stats.size());
+        assertTrue(stats.internalSize() >= 7);
+
+        assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L,
                 2L, 2L, 3);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 2L, 2L,
+        assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2L, 2L,
                 2L, 2L, 4);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 3L,
-                3L, 2L, 2L, 5);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 3L,
-                3L, 2L, 2L, 5);
-
-        assertEquals(4, stats.size());
-        assertEquals(4, stats.internalSize());
-
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4L,
+        assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 3L, 3L,
+                2L, 2L, 5);
+        assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 4L,
                 40L, 4L, 40L, 7);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5L,
+        assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 5L,
                 50L, 4L, 40L, 8);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 6L,
+        assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 6L,
                 60L, 5L, 50L, 10);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 7L,
+        assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 7L,
                 70L, 5L, 50L, 11);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 7L,
-                70L, 5L, 50L, 11);
-
-        assertEquals(9, stats.size());
-        assertTrue(stats.internalSize() >= 9);
-
-        assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1L, 1L, 2L, 2L, 3);
-        assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                2L, 2L, 2L, 2L, 4);
-        assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
-                3L, 3L, 2L, 2L, 5);
-        assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES, 3L, 3L, 2L, 2L, 5);
-        assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                4L, 40L, 4L, 40L, 7);
-        assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                5L, 50L, 4L, 40L, 8);
-        assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                6L, 60L, 5L, 50L, 10);
-        assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
-                7L, 70L, 5L, 50L, 11);
-        assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
-                ROAMING_YES, 7L, 70L, 5L, 50L, 11);
     }
 
     public void testCombineExisting() throws Exception {
@@ -176,18 +152,20 @@
         stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
                 -128L, -1L, -1);
 
-        assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                384L, 3L, 128L, 1L, 9);
-        assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, 128L,
-                1L, 128L, 1L, 2);
+        assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, ROAMING_NO, 384L, 3L,
+                128L, 1L, 9);
+        assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, ROAMING_NO, 128L, 1L, 128L,
+                1L, 2);
 
         // now try combining that should create row
-        stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
-        assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                128L, 1L, 128L, 1L, 3);
-        stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
-        assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                256L, 2L, 256L, 2L, 6);
+        stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
+                128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 1L,
+                128L, 1L, 3);
+        stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
+                128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_NO, 256L, 2L,
+                256L, 2L, 6);
     }
 
     public void testSubtractIdenticalData() throws Exception {
@@ -202,10 +180,10 @@
         final NetworkStats result = after.subtract(before);
 
         // identical data should result in zero delta
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
+                0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
+                0L, 0);
     }
 
     public void testSubtractIdenticalRows() throws Exception {
@@ -220,10 +198,10 @@
         final NetworkStats result = after.subtract(before);
 
         // expect delta between measurements
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L,
-                1L, 2L, 1L, 4);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 3L,
-                1L, 4L, 1L, 8);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L, 2L,
+                1L, 4);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 3L, 1L, 4L,
+                1L, 8);
     }
 
     public void testSubtractNewRows() throws Exception {
@@ -239,12 +217,12 @@
         final NetworkStats result = after.subtract(before);
 
         // its okay to have new rows
-        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
-        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                0L, 0L, 0L, 0);
-        assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1024L, 8L, 1024L, 8L, 20);
+        assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
+                0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
+                0L, 0);
+        assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                1024L, 8L, 20);
     }
 
     public void testSubtractMissingRows() throws Exception {
@@ -259,8 +237,8 @@
 
         // should silently drop omitted rows
         assertEquals(1, result.size());
-        assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 1L, 2L, 3L, 4L, 0);
+        assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L,
+                2L, 3L, 4L, 0);
         assertEquals(4L, result.getTotalBytes());
     }
 
@@ -285,22 +263,13 @@
                 .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
         assertEquals(64L, uidTag.getTotalBytes());
 
-        final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L);
-        assertEquals(96L, uidMetered.getTotalBytes());
-
         final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 32L, 0L,
-                        0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_YES, 32L, 0L, 0L, 0L,
+                        0L);
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
 
@@ -314,95 +283,95 @@
 
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
-                .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, 0L,
+                .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, ROAMING_NO, 128L, 8L, 0L, 2L,
+                        20L)
+                .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
                         2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
-                        8L, 0L, 2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, 128L, 8L, 0L,
-                        2L, 20L);
+                .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, ROAMING_YES, 128L, 8L, 0L, 2L,
+                        20L);
         final NetworkStats grouped = uidStats.groupedByIface();
 
         assertEquals(3, uidStats.size());
         assertEquals(1, grouped.size());
 
-        assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                384L, 24L, 0L, 6L, 0L);
+        assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 384L, 24L, 0L,
+                6L, 0L);
     }
 
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
+                        2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 512L, 32L, 0L,
+                        0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 4L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 512L, 32L,
                         0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
-                        8L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
+                        0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 8L, 0L,
+                        0L, 0L);
 
         final NetworkStats grouped = uidStats.groupedByIface();
 
         assertEquals(7, uidStats.size());
 
         assertEquals(2, grouped.size());
-        assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                384L, 24L, 0L, 2L, 0L);
-        assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
-                1024L, 64L, 0L, 0L, 0L);
+        assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 384L, 24L, 0L,
+                2L, 0L);
+        assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 1024L, 64L,
+                0L, 0L, 0L);
     }
 
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
-                        0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
-                        0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 0L, 0L,
+                        0L, 0L)
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 32L, 0L, 0L,
+                        0L, 0L);
 
         final NetworkStats second = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L,
                         0L, 0L, 0L)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
-                        0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
-                        0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 32L, 0L, 0L,
+                        0L, 0L);
 
         first.combineAllValues(second);
 
         assertEquals(4, first.size());
-        assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 64L,
+        assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 64L, 0L, 0L,
+                0L, 0L);
+        assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 0L,
+                0L, 0L, 0L);
+        assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 64L, 0L,
+                0L, 0L, 0L);
+        assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L,
                 0L, 0L, 0L, 0L);
-        assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 0L, 0L, 0L, 0L);
-        assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
-                64L, 0L, 0L, 0L, 0L);
-        assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 0L, 0L, 0L, 0L);
     }
 
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
+                        2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 512L, 32L, 0L,
+                        0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 4L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 512L, 32L,
                         0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
-                        32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
-                        8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L, 8L,
-                        0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
-                        8L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
+                        0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L, 0L,
+                        0L)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 8L, 0L,
+                        0L, 0L);
 
         assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
         assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L);
@@ -427,10 +396,10 @@
         final NetworkStats after = before.withoutUids(new int[] { 100 });
         assertEquals(6, before.size());
         assertEquals(2, after.size());
-        assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                128L, 8L, 0L, 0L, 0L);
-        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L,
-                8L, 0L, 0L, 0L);
+        assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L,
+                0L, 0L, 0L);
+        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L,
+                0L, 0L);
     }
 
     public void testClone() throws Exception {
@@ -465,175 +434,105 @@
         final String underlyingIface = "wlan0";
         final int testTag1 = 8888;
         NetworkStats delta = new NetworkStats(TEST_START, 17)
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 39605L, 46L,
-                    12259L, 55L, 0L)
-            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                    0L, 0L, 0L)
-            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 72667L, 197L,
-                    43909L, 241L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 9297L,
-                    17L, 4128L, 21L, 0L)
+            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, 39605L, 46L, 12259L, 55L, 0L)
+            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, 72667L, 197L, 43909L, 241L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, 9297L, 17L, 4128L, 21L, 0L)
             // VPN package also uses some traffic through unprotected network.
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4983L, 10L,
-                    1801L, 12L, 0L)
-            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
-                    0L, 0L, 0L)
+            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, 4983L, 10L, 1801L, 12L, 0L)
+            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
             // Tag entries
-            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, 21691L, 41L,
-                    13820L, 51L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, 1281L, 2L,
-                    665L, 2L, 0L)
+            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, 21691L, 41L, 13820L, 51L, 0L)
+            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L)
             // Irrelevant entries
-            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1685L, 5L,
-                    2070L, 6L, 0L)
+            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L)
             // Underlying Iface entries
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5178L,
-                    8L, 2139L, 11L, 0L)
-            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
-                    0L, 0L, 0L, 0L)
-            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    149873L, 287L, 59217L /* smaller than sum(tun0) */,
-                    299L /* smaller than sum(tun0) */, 0L)
-            .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    0L, 0L, 0L, 0L, 0L);
+            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, 5178L, 8L, 2139L, 11L, 0L)
+            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, 149873L, 287L,
+                    59217L /* smaller than sum(tun0) */, 299L /* smaller than sum(tun0) */, 0L)
+            .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
 
         assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
-        assertEquals(20, delta.size());
+        assertEquals(21, delta.size());
 
         // tunIface and TEST_IFACE entries are not changed.
-        assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                 39605L, 46L, 12259L, 55L, 0L);
-        assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                0L, 0L, 0L, 0L, 0L);
-        assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L,
+                0L, 0L, 0L);
+        assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                 72667L, 197L, 43909L, 241L, 0L);
-        assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+        assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
                 9297L, 17L, 4128L, 21L, 0L);
-        assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                 4983L, 10L, 1801L, 12L, 0L);
-        assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                0L, 0L, 0L, 0L, 0L);
-        assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+        assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L,
+                0L, 0L, 0L);
+        assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO,
                 21691L, 41L, 13820L, 51L, 0L);
-        assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
-                1281L, 2L, 665L, 2L, 0L);
-        assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1685L, 5L, 2070L, 6L, 0L);
+        assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO, 1281L,
+                2L, 665L, 2L, 0L);
+        assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1685L, 5L,
+                2070L, 6L, 0L);
 
         // Existing underlying Iface entries are updated
-        assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 44783L, 54L, 14178L, 62L, 0L);
-        assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+        assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+                44783L, 54L, 13829L, 60L, 0L);
+        assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
+                0L, 0L, 0L, 0L, 0L);
 
         // VPN underlying Iface entries are updated
-        assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 28304L, 27L, 1L, 2L, 0L);
-        assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+        assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+                28304L, 27L, 1719L, 12L, 0L);
+        assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
+                0L, 0L, 0L, 0L, 0L);
 
         // New entries are added for new application's underlying Iface traffic
-        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 72667L, 197L, 43123L, 227L, 0L);
-        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO,
-                ROAMING_NO, 9297L, 17L, 4054, 19L, 0L);
-        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO,
-                ROAMING_NO, 21691L, 41L, 13572L, 48L, 0L);
-        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO,
-                ROAMING_NO, 1281L, 2L, 653L, 1L, 0L);
+        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+                72667L, 197L, 41872L, 219L, 0L);
+        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
+                9297L, 17L, 3936, 19L, 0L);
+        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO,
+                21691L, 41L, 13179L, 46L, 0L);
+        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO,
+                1281L, 2L, 634L, 1L, 0L);
 
         // New entries are added for debug purpose
-        assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 39605L, 46L, 12039, 51, 0);
-        assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 81964, 214, 47177, 246, 0);
-        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
-                ROAMING_ALL, 121569, 260, 59216, 297, 0);
+        assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
+                39605L, 46L, 11690, 49, 0);
+        assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
+                81964, 214, 45808, 238, 0);
+        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
+                4983, 10, 1717, 10, 0);
+        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL,
+                126552, 270, 59215, 297, 0);
 
     }
 
-    // Tests a case where all of the data received by the tun0 interface is echo back into the tun0
-    // interface by the vpn app before it's sent out of the underlying interface. The VPN app should
-    // not be charged for the echoed data but it should still be charged for any extra data it sends
-    // via the underlying interface.
-    public void testMigrateTun_VpnAsLoopback() {
-        final int tunUid = 10030;
-        final String tunIface = "tun0";
-        final String underlyingIface = "wlan0";
-        NetworkStats delta = new NetworkStats(TEST_START, 9)
-            // 2 different apps sent/receive data via tun0.
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50000L, 25L,
-                    100000L, 50L, 0L)
-            .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 500L, 2L,
-                    200L, 5L, 0L)
-            // VPN package resends data through the tunnel (with exaggerated overhead)
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 240000,
-                    100L, 120000L, 60L, 0L)
-            // 1 app already has some traffic on the underlying interface, the other doesn't yet
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1000L,
-                    10L, 2000L, 20L, 0L)
-            // Traffic through the underlying interface via the vpn app.
-            // This test should redistribute this data correctly.
-            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    75500L, 37L, 130000L, 70L, 0L);
-
-        assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
-        assertEquals(9, delta.size());
-
-        // tunIface entries should not be changed.
-        assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                50000L, 25L, 100000L, 50L, 0L);
-        assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                500L, 2L, 200L, 5L, 0L);
-        assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                240000L, 100L, 120000L, 60L, 0L);
-
-        // Existing underlying Iface entries are updated
-        assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 51000L, 35L, 102000L, 70L, 0L);
-
-        // VPN underlying Iface entries are updated
-        assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 25000L, 10L, 29800L, 15L, 0L);
-
-        // New entries are added for new application's underlying Iface traffic
-        assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO,
-                ROAMING_NO, 500L, 2L, 200L, 5L, 0L);
-
-        // New entries are added for debug purpose
-        assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 50000L, 25L, 100000L, 50L, 0L);
-        assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
-                ROAMING_NO, 500, 2L, 200L, 5L, 0L);
-        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
-                ROAMING_ALL, 50500L, 27L, 100200L, 55, 0);
-    }
-
     private static void assertContains(NetworkStats stats,  String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, long operations) {
-        int index = stats.findIndex(iface, uid, set, tag, metered, roaming);
+            int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+            long operations) {
+        int index = stats.findIndex(iface, uid, set, tag, roaming);
         assertTrue(index != -1);
-        assertValues(stats, index, iface, uid, set, tag, metered, roaming,
+        assertValues(stats, index, iface, uid, set, tag, roaming,
                 rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, long operations) {
+            int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+            long operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
-        assertValues(entry, iface, uid, set, tag, metered, roaming);
+        assertValues(entry, iface, uid, set, tag, roaming);
         assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private static void assertValues(
-            NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered,
-            int roaming) {
+            NetworkStats.Entry entry, String iface, int uid, int set, int tag, int roaming) {
         assertEquals(iface, entry.iface);
         assertEquals(uid, entry.uid);
         assertEquals(set, entry.set);
         assertEquals(tag, entry.tag);
-        assertEquals(metered, entry.metered);
         assertEquals(roaming, entry.roaming);
     }
 
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 9c3346e..e818c56 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -166,4 +166,52 @@
         assertTrue(newNetwork.meteredHint);
         assertNull(newNetwork.attributes);
     }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadgeWhenNoAttributesBundle() {
+        ScoredNetwork network = new ScoredNetwork(KEY, CURVE);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadgeWhenNoBadgingCurveInBundle() {
+        ScoredNetwork network = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturn4kBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_4K);
+        assertEquals(ScoredNetwork.BADGING_4K, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnHdBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_HD);
+        assertEquals(ScoredNetwork.BADGING_HD, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnSdBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_SD);
+        assertEquals(ScoredNetwork.BADGING_SD, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_NONE);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    private ScoredNetwork buildScoredNetworkWithGivenBadgeForTestRssi(int badge) {
+        RssiCurve badgingCurve =
+               new RssiCurve(RSSI_START, 10, new byte[] {0, 0, 0, 0, 0, 0, (byte) badge});
+        Bundle attr = new Bundle();
+        attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgingCurve);
+        return new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+    }
 }
diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/android/util/TokenBucketTest.java
deleted file mode 100644
index f7ac20c..0000000
--- a/core/tests/coretests/src/android/util/TokenBucketTest.java
+++ /dev/null
@@ -1,179 +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 com.android.internal.util;
-
-import android.os.SystemClock;
-import android.text.format.DateUtils;
-import junit.framework.TestCase;
-
-public class TokenBucketTest extends TestCase {
-
-    static final int FILL_DELTA_VERY_SHORT  = 1;
-    static final int FILL_DELTA_VERY_LONG   = Integer.MAX_VALUE;
-
-    public void testArgumentValidation() {
-        assertThrow(() -> new TokenBucket(0, 1, 1));
-        assertThrow(() -> new TokenBucket(1, 0, 1));
-        assertThrow(() -> new TokenBucket(1, 1, 0));
-        assertThrow(() -> new TokenBucket(0, 1));
-        assertThrow(() -> new TokenBucket(1, 0));
-        assertThrow(() -> new TokenBucket(-1, 1, 1));
-        assertThrow(() -> new TokenBucket(1, -1, 1));
-        assertThrow(() -> new TokenBucket(1, 1, -1));
-        assertThrow(() -> new TokenBucket(-1, 1));
-        assertThrow(() -> new TokenBucket(1, -1));
-
-        new TokenBucket(1000, 100, 0);
-        new TokenBucket(1000, 100, 10);
-        new TokenBucket(5000, 50);
-        new TokenBucket(5000, 1);
-    }
-
-    public void testInitialCapacity() {
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 1), 1);
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10), 10);
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 1000), 1000);
-
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 0), 0);
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 3), 3);
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 10), 10);
-
-        drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 100), 10);
-
-        drain(new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50), 50);
-        drain(new TokenBucket((int)DateUtils.HOUR_IN_MILLIS, 10), 10);
-        drain(new TokenBucket((int)DateUtils.DAY_IN_MILLIS, 200), 200);
-    }
-
-    public void testReset() {
-        TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_LONG, 100, 10);
-        drain(tb, 10);
-
-        tb.reset(50);
-        drain(tb, 50);
-
-        tb.reset(50);
-        getOneByOne(tb, 10);
-        assertTrue(tb.has());
-
-        tb.reset(30);
-        drain(tb, 30);
-    }
-
-    public void testFill() throws Exception {
-        int delta = 50;
-        TokenBucket tb = new TokenBucket(delta, 10, 0);
-
-        assertEmpty(tb);
-
-        Thread.sleep(3 * delta / 2);
-
-        assertTrue(tb.has());
-    }
-
-    public void testRefill() throws Exception {
-        TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_SHORT, 10, 10);
-
-        assertEquals(5, tb.get(5));
-        assertEquals(5, tb.get(5));
-
-        while (tb.available() < 10) {
-            Thread.sleep(2);
-        }
-
-        assertEquals(10, tb.get(10));
-
-        while (tb.available() < 10) {
-            Thread.sleep(2);
-        }
-
-        assertEquals(10, tb.get(100));
-    }
-
-    public void testAverage() throws Exception {
-        final int delta = 3;
-        final int want = 60;
-
-        long start = SystemClock.elapsedRealtime();
-        TokenBucket tb = new TokenBucket(delta, 20, 0);
-
-        for (int i = 0; i < want; i++) {
-            while (!tb.has()) {
-                Thread.sleep(5 * delta);
-            }
-            tb.get();
-        }
-
-        assertDuration(want * delta, SystemClock.elapsedRealtime() - start);
-    }
-
-    public void testBurst() throws Exception {
-        final int delta = 2;
-        final int capacity = 20;
-        final int want = 100;
-
-        long start = SystemClock.elapsedRealtime();
-        TokenBucket tb = new TokenBucket(delta, capacity, 0);
-
-        int total = 0;
-        while (total < want) {
-            while (!tb.has()) {
-                Thread.sleep(capacity * delta - 2);
-            }
-            total += tb.get(tb.available());
-        }
-
-        assertDuration(total * delta, SystemClock.elapsedRealtime() - start);
-    }
-
-    static void getOneByOne(TokenBucket tb, int n) {
-        while (n > 0) {
-            assertTrue(tb.has());
-            assertTrue(tb.available() >= n);
-            assertTrue(tb.get());
-            assertTrue(tb.available() >= n - 1);
-            n--;
-        }
-    }
-
-    void assertEmpty(TokenBucket tb) {
-        assertFalse(tb.has());
-        assertEquals(0, tb.available());
-        assertFalse(tb.get());
-    }
-
-    void drain(TokenBucket tb, int n) {
-        getOneByOne(tb, n);
-        assertEmpty(tb);
-    }
-
-    void assertDuration(long expected, long elapsed) {
-        String msg = String.format(
-            "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
-        elapsed += 1; // one millisecond extra guard
-        assertTrue(msg, elapsed >= expected);
-    }
-
-    void assertThrow(Fn fn)     {
-      try {
-          fn.call();
-          fail("expected n exception to be thrown.");
-      } catch (Throwable t) {}
-    }
-
-    interface Fn { void call(); }
-}
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index 7f13abc..327f3fd 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.net;
 
-import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -102,14 +101,12 @@
 
         final NetworkStats stats = mFactory.readNetworkStatsDetail();
         assertEquals(70, stats.size());
-        assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L,
-                676L);
+        assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L, 676L);
         assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L);
     }
 
     public void testNetworkStatsSingle() throws Exception {
-        stageFile(R.raw.xt_qtaguid_iface_typical,
-                new File(mTestProc, "net/xt_qtaguid/iface_stat_all"));
+        stageFile(R.raw.xt_qtaguid_iface_typical, new File(mTestProc, "net/xt_qtaguid/iface_stat_all"));
 
         final NetworkStats stats = mFactory.readNetworkStatsSummaryDev();
         assertEquals(6, stats.size());
@@ -125,8 +122,7 @@
         final NetworkStats stats = mFactory.readNetworkStatsSummaryXt();
         assertEquals(3, stats.size());
         assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 6824L, 16L, 5692L, 10L);
-        assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L,
-                2468L);
+        assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L, 2468L);
         assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L);
     }
 
@@ -161,7 +157,7 @@
 
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long txBytes) {
-        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+        final int i = stats.findIndex(iface, uid, set, tag, ROAMING_NO);
         final NetworkStats.Entry entry = stats.getValues(i, null);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
@@ -169,7 +165,7 @@
 
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
-        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+        final int i = stats.findIndex(iface, uid, set, tag, ROAMING_NO);
         final NetworkStats.Entry entry = stats.getValues(i, null);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index 9b2b9f1..836ede6 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -23,7 +23,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 8
 
 LOCAL_PACKAGE_NAME := MultiDexLegacyAndException
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index 874263f..2915914 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -23,7 +23,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 8
 
 LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp
 
@@ -45,7 +45,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 8
 
 LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2
 
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 981be11..6d1ebb4 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -14,7 +14,6 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    frameworks-base-testutils \
     mockito-target
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 5d6b3a8..1f1939c 100755
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -294,7 +294,7 @@
 skuList.add("premiumUpgrade");
 skuList.add("gas");
 Bundle querySkus = new Bundle();
-querySkus.putStringArrayList(“ITEM_ID_LIST”, skuList);
+querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
 </pre>
 
 <p>
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 9d5860c..f5bb821 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -142,7 +142,7 @@
 }
 
 uint32_t AnimatorManager::animateCommon(TreeInfo& info) {
-    uint32_t dirtyMask;
+    uint32_t dirtyMask = 0;
     AnimateFunctor functor(info, mAnimationHandle->context(), &dirtyMask);
     auto newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
     mAnimators.erase(newEnd, mAnimators.end());
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ac6a28f..86731c9 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -134,7 +134,12 @@
 void EglManager::initExtensions() {
     auto extensions = StringUtils::split(
             eglQueryString(mEglDisplay, EGL_EXTENSIONS));
-    EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
+    // For our purposes we don't care if EGL_BUFFER_AGE is a result of
+    // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
+    // under EGL_KHR_partial_update and we don't need the expanded scope
+    // that EGL_EXT_buffer_age provides.
+    EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age")
+            || extensions.has("EGL_KHR_partial_update");
     EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
     LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
             "Missing required extension EGL_KHR_swap_buffers_with_damage");
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 33c1c3f..ce75bb49 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3278,6 +3278,20 @@
         return delay;
     }
 
+     /**
+     * Indicate A2DP device configuration has changed.
+     * @param device Bluetooth device whose configuration has changed.
+     * {@hide}
+     */
+    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) {
+        IAudioService service = getService();
+        try {
+            service.handleBluetoothA2dpDeviceConfigChange(device);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** {@hide} */
     public IRingtonePlayer getRingtonePlayer() {
         try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f597440..8a28255 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -678,6 +678,9 @@
     public static native int setDeviceConnectionState(int device, int state,
                                                       String device_address, String device_name);
     public static native int getDeviceConnectionState(int device, String device_address);
+    public static native int handleDeviceConfigChange(int device,
+                                                      String device_address,
+                                                      String device_name);
     public static native int setPhoneState(int state);
     public static native int setForceUse(int usage, int config);
     public static native int getForceUse(int usage);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c7931fc..9e5ac72 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -133,6 +133,8 @@
 
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
+    void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
+
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
 
     boolean isCameraSoundForced();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 949acc0..d0f8b73b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2336,10 +2336,7 @@
     }
 
     private void scanInternalSubtitleTracks() {
-        if (mSubtitleController == null) {
-            Log.d(TAG, "setSubtitleAnchor in MediaPlayer");
-            setSubtitleAnchor();
-        }
+        setSubtitleAnchor();
 
         populateInbandTracks();
 
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 3c2ea58..44c8b0b 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -29,9 +29,21 @@
         System.loadLibrary("media_jni");
     }
 
-    public MtpServer(MtpDatabase database, boolean usePtp) {
+    public MtpServer(
+            MtpDatabase database,
+            boolean usePtp,
+            String deviceInfoManufacturer,
+            String deviceInfoModel,
+            String deviceInfoDeviceVersion,
+            String deviceInfoSerialNumber) {
         mDatabase = database;
-        native_setup(database, usePtp);
+        native_setup(
+                database,
+                usePtp,
+                deviceInfoManufacturer,
+                deviceInfoModel,
+                deviceInfoDeviceVersion,
+                deviceInfoSerialNumber);
         database.setServer(this);
     }
 
@@ -72,7 +84,13 @@
     }
 
     public static native final void native_configure(boolean usePtp);
-    private native final void native_setup(MtpDatabase database, boolean usePtp);
+    private native final void native_setup(
+            MtpDatabase database,
+            boolean usePtp,
+            String deviceInfoManufacturer,
+            String deviceInfoModel,
+            String deviceInfoDeviceVersion,
+            String deviceInfoSerialNumber);
     private native final void native_run();
     private native final void native_cleanup();
     private native final void native_send_object_added(int handle);
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index afd3082..c325f4e 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -61,10 +61,34 @@
 }
 
 static void
-android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp)
+android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp,
+        jstring deviceInfoManufacturer,
+        jstring deviceInfoModel,
+        jstring deviceInfoDeviceVersion,
+        jstring deviceInfoSerialNumber)
 {
+    const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
+    const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
+    const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
+    const char *deviceInfoSerialNumberStr = env->GetStringUTFChars(deviceInfoSerialNumber, NULL);
     MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
-            usePtp, AID_MEDIA_RW, 0664, 0775);
+            usePtp, AID_MEDIA_RW, 0664, 0775,
+            MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
+            MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
+            MtpString((deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : ""),
+            MtpString((deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : ""));
+    if (deviceInfoManufacturerStr != NULL) {
+        env->ReleaseStringUTFChars(deviceInfoManufacturer, deviceInfoManufacturerStr);
+    }
+    if (deviceInfoModelStr != NULL) {
+        env->ReleaseStringUTFChars(deviceInfoModel, deviceInfoModelStr);
+    }
+    if (deviceInfoDeviceVersionStr != NULL) {
+        env->ReleaseStringUTFChars(deviceInfoDeviceVersion, deviceInfoDeviceVersionStr);
+    }
+    if (deviceInfoSerialNumberStr != NULL) {
+        env->ReleaseStringUTFChars(deviceInfoSerialNumber, deviceInfoSerialNumberStr);
+    }
     env->SetLongField(thiz, field_MtpServer_nativeContext, (jlong)server);
 }
 
@@ -180,7 +204,7 @@
 
 static const JNINativeMethod gMethods[] = {
     {"native_configure",              "(Z)V",  (void *)android_mtp_configure},
-    {"native_setup",                "(Landroid/mtp/MtpDatabase;Z)V",
+    {"native_setup",                "(Landroid/mtp/MtpDatabase;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
                                             (void *)android_mtp_MtpServer_setup},
     {"native_run",                  "()V",  (void *)android_mtp_MtpServer_run},
     {"native_cleanup",              "()V",  (void *)android_mtp_MtpServer_cleanup},
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 33c9655..5843637 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -17,4 +17,5 @@
     name: "libandroid.ndk",
     symbol_file: "libandroid.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index e09b0b4..17feb53 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -17,4 +17,5 @@
     name: "libjnigraphics.ndk",
     symbol_file: "libjnigraphics.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index bb8eb2c..b58c87a 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -115,7 +115,6 @@
         myWebView.clearCache(true);
         WebSettings webSettings = myWebView.getSettings();
         webSettings.setJavaScriptEnabled(true);
-        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
         mWebViewClient = new MyWebViewClient();
         myWebView.setWebViewClient(mWebViewClient);
         myWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5b11303..f79cf04 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -100,6 +100,129 @@
         <item>Always use HDCP checking</item>
     </string-array>
 
+
+    <!-- Bluetooth settings -->
+
+    <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] -->
+    <string-array name="bluetooth_a2dp_codec_titles">
+        <item>Default</item>
+        <item>SBC</item>
+        <item>aptX</item>
+        <item>aptX-HD</item>
+        <item>LDAC</item>
+    </string-array>
+
+    <!-- Values for Bluetooth Audio Codec selection preference. -->
+    <string-array name="bluetooth_a2dp_codec_values" translatable="false" >
+        <item>1000000</item>
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+    </string-array>
+
+    <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]-->
+    <string-array name="bluetooth_a2dp_codec_summaries" >
+        <item>Default</item>
+        <item>SBC</item>
+        <item>aptX</item>
+        <item>aptX-HD</item>
+        <item>LDAC</item>
+    </string-array>
+
+    <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] -->
+    <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
+        <item>Default</item>
+        <item>44.1 kHz</item>
+        <item>48.0 kHz</item>
+        <item>88.2 kHz</item>
+        <item>96.0 kHz</item>
+    </string-array>
+
+    <!-- Values for Bluetooth Audio Codec Sample Rate selection preference. -->
+    <string-array name="bluetooth_a2dp_codec_sample_rate_values" translatable="false" >
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>4</item>
+        <item>8</item>
+    </string-array>
+
+    <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]-->
+    <string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
+        <item>Default</item>
+        <item>44.1 kHz</item>
+        <item>48.0 kHz</item>
+        <item>88.2 kHz</item>
+        <item>96.0 kHz</item>
+    </string-array>
+
+    <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] -->
+    <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
+        <item>Default</item>
+        <item>16 bits/sample</item>
+        <item>24 bits/sample</item>
+        <item>32 bits/sample</item>
+    </string-array>
+
+    <!-- Values for Bluetooth Audio Codec Bits Per Sample selection preference. -->
+    <string-array name="bluetooth_a2dp_codec_bits_per_sample_values" translatable="false" >
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>4</item>
+    </string-array>
+
+    <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]-->
+    <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
+        <item>Default</item>
+        <item>16 bits/sample</item>
+        <item>24 bits/sample</item>
+        <item>32 bits/sample</item>
+    </string-array>
+
+    <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] -->
+    <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
+        <item>Default</item>
+        <item>Mono</item>
+        <item>Stereo</item>
+    </string-array>
+
+    <!-- Values for Bluetooth Audio Codec Channel Mode selection preference. -->
+    <string-array name="bluetooth_a2dp_codec_channel_mode_values" translatable="false" >
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+    </string-array>
+
+    <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]-->
+    <string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
+        <item>Default</item>
+        <item>Mono</item>
+        <item>Stereo</item>
+    </string-array>
+
+    <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40] -->
+    <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
+        <item>Sound quality preferred (990kbps/909kbps)</item>
+        <item>Standard (660kbps/606kbps)</item>
+        <item>Connection preferred (330kbps/303kbps)</item>
+    </string-array>
+
+    <!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. -->
+    <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_values" translatable="false" >
+        <item>1000</item>
+        <item>1001</item>
+        <item>1002</item>
+    </string-array>
+
+    <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40]-->
+    <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" >
+        <item>Sound quality preferred (990kbps/909kbps)</item>
+        <item>Standard (660kbps/606kbps)</item>
+        <item>Connection preferred (330kbps/303kbps)</item>
+    </string-array>
+
     <!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
     <string-array name="select_logd_size_titles">
         <item>Off</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 29839e7..f389000 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -428,6 +428,31 @@
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
 
+    <!-- UI debug setting: Select Bluetooth Audio Codec -->
+    <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
+    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec -->
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Preferred Bluetooth A2DP Codec</string>
+
+    <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
+    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Preferred Bluetooth A2DP Codec Sample Rate</string>
+
+    <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
+    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Preferred Bluetooth A2DP Codec Bits Per Sample</string>
+
+    <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
+    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Preferred Bluetooth A2DP Codec Channel Mode</string>
+
+    <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Playback Quality</string>
+    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec LDAC Playback Quality -->
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Preferred Bluetooth A2DP Codec LDAC Playback Quality</string>
+
     <!-- setting Checkbox summary whether to show options for wireless display certification  -->
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
     <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 5c8a6e2..a172e19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -358,6 +358,11 @@
     private final TouchAnimator.Listener mNonFirstPageListener =
             new TouchAnimator.ListenerAdapter() {
                 @Override
+                public void onAnimationAtEnd() {
+                    mQuickQsPanel.setVisibility(View.INVISIBLE);
+                }
+
+                @Override
                 public void onAnimationStarted() {
                     mQuickQsPanel.setVisibility(View.VISIBLE);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index dfc89fa..612eba7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -345,7 +345,10 @@
 
             @Override
             public void onAnnouncementRequested(CharSequence announcement) {
-                announceForAccessibility(announcement);
+                if (announcement != null) {
+                    mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
+                            .sendToTarget();
+                }
             }
         };
         r.tile.addCallback(callback);
@@ -514,10 +517,13 @@
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
+        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == SHOW_DETAIL) {
                 handleShowDetail((Record)msg.obj, msg.arg1 != 0);
+            } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
+                announceForAccessibility((CharSequence)msg.obj);
             }
         }
     }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 86bbca7..00d1880 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -57,14 +57,16 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Map;
 
+
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
@@ -137,16 +139,46 @@
         new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
+
     // used inside handler thread
     private boolean mQuietEnable = false;
-    // configuarion from external IBinder call which is used to
+    private boolean mEnable;
+
+    /**
+     * Used for tracking apps that enabled / disabled Bluetooth.
+     */
+    private class ActiveLog {
+        private String mPackageName;
+        private boolean mEnable;
+        private long mTimestamp;
+
+        public ActiveLog(String packageName, boolean enable, long timestamp) {
+            mPackageName = packageName;
+            mEnable = enable;
+            mTimestamp = timestamp;
+        }
+
+        public long getTime() {
+            return mTimestamp;
+        }
+
+        public String toString() {
+            return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) +
+                    (mEnable ? "  Enabled " : " Disabled ") + " by " + mPackageName;
+        }
+
+    }
+
+    private LinkedList<ActiveLog> mActiveLogs;
+
+    // configuration from external IBinder call which is used to
     // synchronize with broadcast receiver.
     private boolean mQuietEnableExternal;
-    // configuarion from external IBinder call which is used to
-    // synchronize with broadcast receiver.
     private boolean mEnableExternal;
-    // used inside handler thread
-    private boolean mEnable;
+
+    // Map of apps registered to keep BLE scanning on.
+    private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
+
     private int mState;
     private final BluetoothHandler mHandler;
     private int mErrorRecoveryRetryCounter;
@@ -172,7 +204,7 @@
         }
     }
 
-    private final IBluetoothCallback mBluetoothCallback =  new IBluetoothCallback.Stub() {
+    private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
         @Override
         public void onBluetoothStateChange(int prevState, int newState) throws RemoteException  {
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
@@ -251,12 +283,12 @@
                         } else if (st == BluetoothAdapter.STATE_ON){
                             // disable without persisting the setting
                             Slog.d(TAG, "Calling disable");
-                            sendDisableMsg();
+                            sendDisableMsg("airplane mode");
                         }
                     } else if (mEnableExternal) {
                         // enable without persisting the setting
                         Slog.d(TAG, "Calling enable");
-                        sendEnableMsg(mQuietEnableExternal);
+                        sendEnableMsg(mQuietEnableExternal, "airplane mode");
                     }
                 }
             }
@@ -272,6 +304,7 @@
                     || context.getResources().getBoolean(
                 com.android.internal.R.bool.config_permissionReviewRequired);
 
+        mActiveLogs = new LinkedList<ActiveLog>();
         mBluetooth = null;
         mBluetoothBinder = null;
         mBluetoothGatt = null;
@@ -298,15 +331,15 @@
             mEnableExternal = true;
         }
 
-        int sysUiUid = -1;
+        int systemUiUid = -1;
         try {
-            sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
+            systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
                     PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
         }
-        mSystemUiUid = sysUiUid;
+        mSystemUiUid = systemUiUid;
     }
 
     /**
@@ -484,8 +517,14 @@
     }
 
     class ClientDeathRecipient implements IBinder.DeathRecipient {
+        private String mPackageName;
+
+        public ClientDeathRecipient(String packageName) {
+            mPackageName = packageName;
+        }
+
         public void binderDied() {
-            if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
+            if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
             if (isBleAppPresent()) {
               // Nothing to do, another app is here.
               return;
@@ -504,10 +543,11 @@
                 mBluetoothLock.readLock().unlock();
             }
         }
-    }
 
-    /** Internal death rec list */
-    Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
+        public String getPackageName() {
+            return mPackageName;
+        }
+    }
 
     @Override
     public boolean isBleScanAlwaysAvailable() {
@@ -565,28 +605,22 @@
         }
     }
 
-    public int updateBleAppCount(IBinder token, boolean enable) {
-        if (enable) {
-            ClientDeathRecipient r = mBleApps.get(token);
-            if (r == null) {
-                ClientDeathRecipient deathRec = new ClientDeathRecipient();
-                try {
-                    token.linkToDeath(deathRec, 0);
-                } catch (RemoteException ex) {
-                    throw new IllegalArgumentException("Wake lock is already dead.");
-                }
-                mBleApps.put(token, deathRec);
-                if (DBG) Slog.d(TAG, "Registered for death Notification");
+    public int updateBleAppCount(IBinder token, boolean enable, String packageName) {
+        ClientDeathRecipient r = mBleApps.get(token);
+        if (r == null && enable) {
+            ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName);
+            try {
+                token.linkToDeath(deathRec, 0);
+            } catch (RemoteException ex) {
+                throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
             }
-
-        } else  {
-            ClientDeathRecipient r = mBleApps.get(token);
-            if (r != null) {
-                // Unregister death recipient as the app goes away.
-                token.unlinkToDeath(r, 0);
-                mBleApps.remove(token);
-                if (DBG) Slog.d(TAG, "Unregistered for death Notification");
-            }
+            mBleApps.put(token, deathRec);
+            if (DBG) Slog.d(TAG, "Registered for death of " + packageName);
+        } else if (!enable && r != null) {
+            // Unregister death recipient as the app goes away.
+            token.unlinkToDeath(r, 0);
+            mBleApps.remove(token);
+            if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName);
         }
         int appCount = mBleApps.size();
         if (DBG) Slog.d(TAG, appCount + " registered Ble Apps");
@@ -601,7 +635,7 @@
         mBleApps.clear();
     }
 
-    /** @hide*/
+    /** @hide */
     public boolean isBleAppPresent() {
         if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
         return mBleApps.size() > 0;
@@ -667,7 +701,7 @@
         }
     }
 
-    public boolean enableNoAutoConnect()
+    public boolean enableNoAutoConnect(String packageName)
     {
         if (isBluetoothDisallowed()) {
             if (DBG) {
@@ -692,7 +726,7 @@
         synchronized(mReceiver) {
             mQuietEnableExternal = true;
             mEnableExternal = true;
-            sendEnableMsg(true);
+            sendEnableMsg(true, packageName);
         }
         return true;
     }
@@ -725,7 +759,7 @@
         }
 
         if (DBG) {
-            Slog.d(TAG,"enable():  mBluetooth =" + mBluetooth +
+            Slog.d(TAG,"enable(" + packageName + "):  mBluetooth =" + mBluetooth +
                     " mBinding = " + mBinding + " mState = " +
                     BluetoothAdapter.nameForState(mState));
         }
@@ -734,7 +768,7 @@
             mQuietEnableExternal = false;
             mEnableExternal = true;
             // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false);
+            sendEnableMsg(false, packageName);
         }
         if (DBG) Slog.d(TAG, "enable returning");
         return true;
@@ -770,7 +804,7 @@
                 persistBluetoothSetting(BLUETOOTH_OFF);
             }
             mEnableExternal = false;
-            sendDisableMsg();
+            sendDisableMsg(packageName);
         }
         return true;
     }
@@ -915,7 +949,7 @@
         }
         if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
             if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth.");
-            sendEnableMsg(mQuietEnableExternal);
+            sendEnableMsg(mQuietEnableExternal, "system boot");
         } else if (!isNameAndAddressSet()) {
             if (DBG) Slog.d(TAG, "Getting adapter name and address");
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
@@ -1883,13 +1917,24 @@
         return false;
     }
 
-    private void sendDisableMsg() {
+    private void sendDisableMsg(String packageName) {
         mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
+        addActiveLog(packageName, false);
     }
 
-    private void sendEnableMsg(boolean quietMode) {
+    private void sendEnableMsg(boolean quietMode, String packageName) {
         mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
                              quietMode ? 1 : 0, 0));
+        addActiveLog(packageName, true);
+    }
+
+    private void addActiveLog(String packageName, boolean enable) {
+        synchronized (mActiveLogs) {
+            if (mActiveLogs.size() > 10) {
+                mActiveLogs.remove();
+            }
+            mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis()));
+        }
     }
 
     private void recoverBluetoothServiceFromError(boolean clearBle) {
@@ -1960,19 +2005,55 @@
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
         String errorMsg = null;
+
+        boolean protoOut = (args.length > 0) && args[0].startsWith("--proto");
+
+        if (!protoOut) {
+            writer.println("Bluetooth Status");
+            writer.println("  enabled: " + isEnabled());
+            writer.println("  state: " + BluetoothAdapter.nameForState(mState));
+            writer.println("  address: " + mAddress);
+            writer.println("  name: " + mName);
+            if (mEnable) {
+                long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime();
+                String onDurationString = String.format("%02d:%02d:%02d.%03d",
+                                          (int)(onDuration / (1000 * 60 * 60)),
+                                          (int)((onDuration / (1000 * 60)) % 60),
+                                          (int)((onDuration / 1000) % 60),
+                                          (int)(onDuration % 1000));
+                writer.println("  time since enabled: " + onDurationString + "\n");
+            }
+
+            writer.println("Enable log:");
+            for (ActiveLog log : mActiveLogs) {
+                writer.println(log);
+            }
+
+            writer.println("\n" + mBleApps.size() + " BLE Apps registered:");
+            for (ClientDeathRecipient app : mBleApps.values()) {
+                writer.println(app.getPackageName());
+            }
+
+            writer.flush();
+            if (args.length == 0) {
+              // Add arg to produce output
+              args = new String[1];
+              args[0] = "--print";
+            }
+        }
+
         if (mBluetoothBinder == null) {
             errorMsg = "Bluetooth Service not connected";
         } else {
             try {
                 mBluetoothBinder.dump(fd, args);
             } catch (RemoteException re) {
-                errorMsg = "RemoteException while calling Bluetooth Service";
+                errorMsg = "RemoteException while dumping Bluetooth Service";
             }
         }
         if (errorMsg != null) {
             // Silently return if we are extracting metrics in Protobuf format
-            if ((args.length > 0) && args[0].startsWith("--proto"))
-                return;
+            if (protoOut) return;
             writer.println(errorMsg);
         }
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 66f5c4d..9dc5f10 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1804,14 +1804,11 @@
     private void updateMtu(LinkProperties newLp, LinkProperties oldLp) {
         final String iface = newLp.getInterfaceName();
         final int mtu = newLp.getMtu();
-        if (oldLp == null && mtu == 0) {
-            // Silently ignore unset MTU value.
-            return;
-        }
         if (oldLp != null && newLp.isIdenticalMtu(oldLp)) {
             if (VDBG) log("identical MTU - not setting");
             return;
         }
+
         if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address()) == false) {
             if (mtu != 0) loge("Unexpected mtu value: " + mtu + ", " + iface);
             return;
@@ -2257,19 +2254,11 @@
                     synchronized (mNetworkForNetId) {
                         nai = mNetworkForNetId.get(netId);
                     }
-                    // If captive portal status has changed, update capabilities or disconnect.
+                    // If captive portal status has changed, update capabilities.
                     if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
                         final int oldScore = nai.getCurrentScore();
                         nai.lastCaptivePortalDetected = visible;
                         nai.everCaptivePortalDetected |= visible;
-                        if (nai.lastCaptivePortalDetected &&
-                            Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
-                            if (DBG) log("Avoiding captive portal network: " + nai.name());
-                            nai.asyncChannel.sendMessage(
-                                    NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
-                            teardownUnneededNetwork(nai);
-                            break;
-                        }
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
                     }
                     if (!visible) {
@@ -2290,12 +2279,6 @@
             return true;
         }
 
-        private int getCaptivePortalMode() {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.CAPTIVE_PORTAL_MODE,
-                    Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
-        }
-
         private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
             switch (msg.what) {
                 default:
@@ -2605,28 +2588,14 @@
                 "request NetworkCapabilities", ConnectivityManager.CALLBACK_CAP_CHANGED);
     }
 
-    private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
-        if (mNetworkRequests.get(nri.request) != null && mNetworkForRequestId.get(
-                nri.request.requestId) == null) {
-            handleRemoveNetworkRequest(nri, ConnectivityManager.CALLBACK_UNAVAIL);
-        }
-    }
-
     private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
         final NetworkRequestInfo nri = getNriForAppRequest(
                 request, callingUid, "release NetworkRequest");
-        if (nri != null) {
-            handleRemoveNetworkRequest(nri, ConnectivityManager.CALLBACK_RELEASED);
-        }
-    }
+        if (nri == null) return;
 
-    private void handleRemoveNetworkRequest(final NetworkRequestInfo nri, final int whichCallback) {
-        final String logCallbackType = ConnectivityManager.getCallbackName(whichCallback);
-        if (VDBG || (DBG && nri.request.isRequest())) {
-            log("releasing " + nri.request + " (" + logCallbackType + ")");
-        }
+        if (VDBG || (DBG && nri.request.isRequest())) log("releasing " + request);
         nri.unlinkDeathRecipient();
-        mNetworkRequests.remove(nri.request);
+        mNetworkRequests.remove(request);
         synchronized (mUidToNetworkRequestCount) {
             int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
             if (requests < 1) {
@@ -2720,7 +2689,7 @@
                 }
             }
         }
-        callCallbackForRequest(nri, null, whichCallback, 0);
+        callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED, 0);
     }
 
     @Override
@@ -2957,11 +2926,6 @@
                     handleRegisterNetworkRequestWithIntent(msg);
                     break;
                 }
-                case EVENT_TIMEOUT_NETWORK_REQUEST: {
-                    NetworkRequestInfo nri = (NetworkRequestInfo) msg.obj;
-                    handleTimedOutNetworkRequest(nri);
-                    break;
-                }
                 case EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT: {
                     handleReleaseNetworkRequestWithIntent((PendingIntent) msg.obj, msg.arg1);
                     break;
@@ -5322,7 +5286,6 @@
     // notify only this one new request of the current state
     protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
         int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
-        mHandler.removeMessages(EVENT_TIMEOUT_NETWORK_REQUEST, nri);
         if (nri.mPendingIntent == null) {
             callCallbackForRequest(nri, nai, notifyType, 0);
         } else {
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index 1b85632..06abca9 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -37,7 +37,7 @@
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
-            publishBinderService(ContextHubService.CONTEXTHUB_SERVICE, mContextHubService);
+            publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService);
         }
     }
 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 97f913e..4405c1b 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -49,6 +49,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.IMaintenanceActivityListener;
 import android.os.Looper;
@@ -1237,7 +1238,7 @@
         }
     }
 
-    public class LocalService {
+    public final class LocalService {
         public void addPowerSaveTempWhitelistAppDirect(int appId, long duration, boolean sync,
                 String reason) {
             addPowerSaveTempWhitelistAppDirectInternal(0, appId, duration, sync, reason);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 74ff41c..6296375 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -154,7 +154,8 @@
 3110 unknown_sources_enabled (value|1)
 # Package Manager critical info
 3120 pm_critical_info (msg|3)
-
+# Disk usage stats for verifying quota correctness
+3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
 
 # ---------------------------
 # WindowManagerService.java
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index b9a4831..4cc2931 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -46,6 +46,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.ObbInfo;
+import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.DropBoxManager;
@@ -86,6 +87,7 @@
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.Xml;
@@ -3738,6 +3740,18 @@
 
             pw.println();
             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
+            final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize();
+            if (pair == null) {
+                pw.println("Internal storage total size: N/A");
+            } else {
+                pw.print("Internal storage (");
+                pw.print(pair.first);
+                pw.print(") total size: ");
+                pw.print(pair.second);
+                pw.print(" (");
+                pw.print((float) pair.second / TrafficStats.GB_IN_BYTES);
+                pw.println(" GB)");
+            }
             pw.println("Force adoptable: " + mForceAdoptable);
             pw.println();
             pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 496d5d0..cef459a 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -34,6 +34,7 @@
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
 import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.net.RecommendationRequest;
@@ -146,37 +147,39 @@
         }
 
         private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
-            if (mPackagesToWatch.contains(scorerPackageName)) {
+            if (!mPackagesToWatch.contains(scorerPackageName)) {
+                // Early exit when we don't care about the package that has changed.
+                return;
+            }
+
+            if (DBG) {
+                Log.d(TAG, "Evaluating binding for: " + scorerPackageName
+                        + ", forceUnbind=" + forceUnbind);
+            }
+            final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+            if (activeScorer == null) {
+                // Package change has invalidated a scorer, this will also unbind any service
+                // connection.
+                if (DBG) Log.d(TAG, "No active scorers available.");
+                unbindFromScoringServiceIfNeeded();
+            } else if (activeScorer.packageName.equals(scorerPackageName)) {
+                // The active scoring service changed in some way.
                 if (DBG) {
-                    Log.d(TAG, "Evaluating binding for: " + scorerPackageName
-                            + ", forceUnbind=" + forceUnbind);
-                }
-                final NetworkScorerAppData activeScorer =
-                        mNetworkScorerAppManager.getActiveScorer();
-                if (activeScorer == null) {
-                    // Package change has invalidated a scorer, this will also unbind any service
-                    // connection.
-                    if (DBG) Log.d(TAG, "No active scorers available.");
-                    unbindFromScoringServiceIfNeeded();
-                } else if (activeScorer.packageName.equals(scorerPackageName)) {
-                    // The active scoring service changed in some way.
-                    if (DBG) {
-                        Log.d(TAG, "Possible change to the active scorer: "
+                    Log.d(TAG, "Possible change to the active scorer: "
                             + activeScorer.packageName);
-                    }
-                    if (forceUnbind) {
-                        unbindFromScoringServiceIfNeeded();
-                    }
-                    bindToScoringServiceIfNeeded(activeScorer);
-                } else {
-                    // One of the scoring apps on the device has changed and we may no longer be
-                    // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
-                    // will sort that out to leave us bound to the most recent active scorer.
-                    if (DBG) {
-                        Log.d(TAG, "Binding to " + activeScorer.packageName + " if needed.");
-                    }
-                    bindToScoringServiceIfNeeded(activeScorer);
                 }
+                if (forceUnbind) {
+                    unbindFromScoringServiceIfNeeded();
+                }
+                bindToScoringServiceIfNeeded(activeScorer);
+            } else {
+                // One of the scoring apps on the device has changed and we may no longer be
+                // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
+                // will sort that out to leave us bound to the most recent active scorer.
+                if (DBG) {
+                    Log.d(TAG, "Binding to " + activeScorer.packageName + " if needed.");
+                }
+                bindToScoringServiceIfNeeded(activeScorer);
             }
         }
     }
@@ -377,13 +380,16 @@
         }
     }
 
+    private boolean isCallerSystemUid() {
+        // REQUEST_NETWORK_SCORES is a signature only permission.
+        return mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES) ==
+                 PackageManager.PERMISSION_GRANTED;
+    }
+
     @Override
     public boolean clearScores() {
-        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
-        // should be allowed to flush all scores.
-        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
-                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
-                        PackageManager.PERMISSION_GRANTED) {
+        // Only the active scorer or the system should be allowed to flush all scores.
+        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
             final long token = Binder.clearCallingIdentity();
             try {
                 clearInternal();
@@ -406,7 +412,6 @@
         // In the future, should this API be opened to 3p apps, we will need to lock this down and
         // figure out another way to streamline the UX.
 
-        // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
         mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
 
         // Scorers (recommendation providers) are selected and no longer set.
@@ -415,11 +420,8 @@
 
     @Override
     public void disableScoring() {
-        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
-        // should be allowed to disable scoring.
-        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
-                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
-                        PackageManager.PERMISSION_GRANTED) {
+        // Only the active scorer or the system should be allowed to disable scoring.
+        if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
             // no-op for now but we could write to the setting if needed.
         } else {
             throw new SecurityException(
@@ -447,7 +449,7 @@
     public void registerNetworkScoreCache(int networkType,
                                           INetworkScoreCache scoreCache,
                                           int filterType) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mScoreCaches) {
@@ -472,7 +474,7 @@
 
     @Override
     public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mScoreCaches) {
@@ -493,7 +495,7 @@
 
     @Override
     public RecommendationResult requestRecommendation(RecommendationRequest request) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
         throwIfCalledOnMainThread();
         final long token = Binder.clearCallingIdentity();
         try {
@@ -523,7 +525,7 @@
 
     @Override
     public boolean requestScores(NetworkKey[] networks) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
         final long token = Binder.clearCallingIdentity();
         try {
             final INetworkRecommendationProvider provider = getRecommendationProvider();
@@ -631,7 +633,7 @@
 
         void connect(Context context) {
             if (!mBound) {
-                Intent service = new Intent();
+                Intent service = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
                 service.setComponent(mComponentName);
                 mBound = context.bindServiceAsUser(service, this,
                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ae1aef6..10a5388 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -369,7 +369,7 @@
         // we do not start the service and launch a review activity if the calling app
         // is in the foreground passing it a pending intent to start the service when
         // review is completed.
-        if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                     callingUid, service, callerFg, userId)) {
                 return null;
@@ -913,7 +913,7 @@
         // we schedule binding to the service but do not start its process, then
         // we launch a review activity to which is passed a callback to invoke
         // when done to start the bound service's process to completing the binding.
-        if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     s.packageName, s.userId)) {
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b02c79..a89dbb6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1575,8 +1575,6 @@
     // being called for multiwindow assist in a single session.
     private int mViSessionId = 1000;
 
-    final boolean mPermissionReviewRequired;
-
     final class KillHandler extends Handler {
         static final int KILL_PROCESS_GROUP_MSG = 4000;
 
@@ -2624,9 +2622,6 @@
 
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
 
-        mPermissionReviewRequired = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_permissionReviewRequired);
-
         mHandlerThread = new ServiceThread(TAG,
                 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
         mHandlerThread.start();
@@ -3729,13 +3724,14 @@
                  * resources like shared libraries and access user-wide resources
                  */
                 if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[2];
+                    gids = new int[3];
                 } else {
-                    gids = new int[permGids.length + 2];
-                    System.arraycopy(permGids, 0, gids, 2, permGids.length);
+                    gids = new int[permGids.length + 3];
+                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
                 }
                 gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
-                gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
             }
             checkTime(startTime, "startProcess: building args");
             if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
@@ -10838,7 +10834,7 @@
                     // If permissions need a review before any of the app components can run,
                     // we return no provider and launch a review activity if the calling app
                     // is in the foreground.
-                    if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
+                    if (Build.PERMISSIONS_REVIEW_REQUIRED) {
                         if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
                             return null;
                         }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 907394e..115971f 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -416,8 +416,7 @@
         // If permissions need a review before any of the app components can run, we
         // launch the review activity and pass a pending intent to start the activity
         // we are to launching now after the review is completed.
-        if ((mService.mPermissionReviewRequired
-                || Build.PERMISSIONS_REVIEW_REQUIRED) && aInfo != null) {
+        if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 IIntentSender target = mService.getIntentSenderLocked(
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index ea901ce..362a347 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -626,7 +626,7 @@
         // the broadcast and if the calling app is in the foreground and the broadcast is
         // explicit we launch the review UI passing it a pending intent to send the skipped
         // broadcast.
-        if (mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
                     filter.owningUserId)) {
                 r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
@@ -1132,8 +1132,7 @@
             // the broadcast and if the calling app is in the foreground and the broadcast is
             // explicit we launch the review UI passing it a pending intent to send the skipped
             // broadcast.
-            if ((mService.mPermissionReviewRequired
-                    || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
+            if (Build.PERMISSIONS_REVIEW_REQUIRED && !skip) {
                 if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
                         info.activityInfo.packageName, UserHandle.getUserId(
                                 info.activityInfo.applicationInfo.uid))) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7fe6c3e..5d619c1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -225,6 +225,7 @@
     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
     private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101;
     private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
+    private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
     // end of messages handled under wakelock
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -3146,7 +3147,7 @@
                             queueMsgUnderWakeLock(mAudioHandler,
                                     MSG_SET_A2DP_SINK_CONNECTION_STATE,
                                     state,
-                                    0,
+                                    0 /* arg2 unused */,
                                     btDevice,
                                     delay);
                         }
@@ -3163,7 +3164,7 @@
                         queueMsgUnderWakeLock(mAudioHandler,
                                 MSG_SET_A2DP_SRC_CONNECTION_STATE,
                                 state,
-                                0,
+                                0 /* arg2 unused */,
                                 btDevice,
                                 0 /* delay */);
                     }
@@ -3809,8 +3810,8 @@
             int delay = checkSendBecomingNoisyIntent(type, state);
             queueMsgUnderWakeLock(mAudioHandler,
                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
-                    0,
-                    0,
+                    0 /* arg1 unused */,
+                    0 /* arg2 unused */,
                     new WiredDeviceConnectionState(type, state, address, name, caller),
                     delay);
         }
@@ -3833,13 +3834,25 @@
                     (profile == BluetoothProfile.A2DP ?
                         MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
                     state,
-                    0,
+                    0 /* arg2 unused */,
                     device,
                     delay);
         }
         return delay;
     }
 
+    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
+    {
+        synchronized (mConnectedDevices) {
+            queueMsgUnderWakeLock(mAudioHandler,
+                    MSG_A2DP_DEVICE_CONFIG_CHANGE,
+                    0 /* arg1 unused */,
+                    0 /* arg1 unused */,
+                    device,
+                    0 /* delay */);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Inner classes
     ///////////////////////////////////////////////////////////////////////////
@@ -4644,6 +4657,11 @@
                     mAudioEventWakeLock.release();
                     break;
 
+                case MSG_A2DP_DEVICE_CONFIG_CHANGE:
+                    onBluetoothA2dpDeviceConfigChange((BluetoothDevice)msg.obj);
+                    mAudioEventWakeLock.release();
+                    break;
+
                 case MSG_REPORT_NEW_ROUTES: {
                     int N = mRoutesObservers.beginBroadcast();
                     if (N > 0) {
@@ -4866,7 +4884,7 @@
     private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
     {
         if (DEBUG_VOL) {
-            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice="+btDevice+"state="+state);
+            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice+"state=" + state);
         }
         if (btDevice == null) {
             return;
@@ -4877,9 +4895,9 @@
         }
 
         synchronized (mConnectedDevices) {
-            String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                                           btDevice.getAddress());
-            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                                                 btDevice.getAddress());
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
             boolean isConnected = deviceSpec != null;
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -4930,7 +4948,7 @@
     private void onSetA2dpSourceConnectionState(BluetoothDevice btDevice, int state)
     {
         if (DEBUG_VOL) {
-            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state);
+            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" + state);
         }
         if (btDevice == null) {
             return;
@@ -4941,8 +4959,8 @@
         }
 
         synchronized (mConnectedDevices) {
-            String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
-            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            final String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
             boolean isConnected = deviceSpec != null;
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -4953,6 +4971,31 @@
         }
     }
 
+    private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
+    {
+        if (DEBUG_VOL) {
+            Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
+        }
+        if (btDevice == null) {
+            return;
+        }
+        String address = btDevice.getAddress();
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            address = "";
+        }
+
+        int device = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+        synchronized (mConnectedDevices) {
+            final String key = makeDeviceListKey(device, address);
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            if (deviceSpec != null) {
+                // Device is connected
+                AudioSystem.handleDeviceConfigChange(device, address,
+                        btDevice.getName());
+            }
+        }
+    }
+
     public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
         synchronized (mA2dpAvrcpLock) {
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index f1d01e0..f1ef947 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -48,10 +48,6 @@
         final IpConnectivityLog log = new IpConnectivityLog();
         log.events = toProto(events);
         log.droppedEvents = dropped;
-        if ((log.events.length > 0) || (dropped > 0)) {
-            // Only write version number if log has some information at all.
-            log.version = IpConnectivityMetrics.VERSION;
-        }
         return IpConnectivityLog.toByteArray(log);
     }
 
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index be68173..28e724c 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -19,25 +19,19 @@
 import android.content.Context;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
-import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.os.IBinder;
 import android.os.Parcelable;
-import android.provider.Settings;
 import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Log;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.TokenBucket;
 import com.android.server.SystemService;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.function.ToIntFunction;
 
 import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
 
@@ -46,21 +40,10 @@
     private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
     private static final boolean DBG = false;
 
-    // The logical version numbers of ipconnectivity.proto, corresponding to the
-    // "version" field of IpConnectivityLog.
-    private static final int NYC      = 0;
-    private static final int NYC_MR1  = 1;
-    private static final int NYC_MR2  = 2;
-    public static final int VERSION   = NYC_MR2;
-
     private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
 
     // Default size of the event buffer. Once the buffer is full, incoming events are dropped.
     private static final int DEFAULT_BUFFER_SIZE = 2000;
-    // Maximum size of the event buffer.
-    private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
-
-    private static final int ERROR_RATE_LIMITED = -1;
 
     // Lock ensuring that concurrent manipulations of the event buffer are correct.
     // There are three concurrent operations to synchronize:
@@ -79,19 +62,10 @@
     private int mDropped;
     @GuardedBy("mLock")
     private int mCapacity;
-    @GuardedBy("mLock")
-    private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
-
-    private final ToIntFunction<Context> mCapacityGetter;
-
-    public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
-        super(ctx);
-        mCapacityGetter = capacityGetter;
-        initBuffer();
-    }
 
     public IpConnectivityMetrics(Context ctx) {
-        this(ctx, READ_BUFFER_SIZE);
+        super(ctx);
+        initBuffer();
     }
 
     @Override
@@ -112,7 +86,7 @@
 
     @VisibleForTesting
     public int bufferCapacity() {
-        return mCapacityGetter.applyAsInt(getContext());
+        return DEFAULT_BUFFER_SIZE; // TODO: read from config
     }
 
     private void initBuffer() {
@@ -130,10 +104,6 @@
             if (event == null) {
                 return left;
             }
-            if (isRateLimited(event)) {
-                // Do not count as a dropped event. TODO: consider adding separate counter
-                return ERROR_RATE_LIMITED;
-            }
             if (left == 0) {
                 mDropped++;
                 return 0;
@@ -143,11 +113,6 @@
         }
     }
 
-    private boolean isRateLimited(ConnectivityMetricsEvent event) {
-        TokenBucket tb = mBuckets.get(event.data.getClass());
-        return (tb != null) && !tb.get();
-    }
-
     private String flushEncodedOutput() {
         final ArrayList<ConnectivityMetricsEvent> events;
         final int dropped;
@@ -221,7 +186,6 @@
         static final String CMD_FLUSH   = "flush";
         static final String CMD_LIST    = "list";
         static final String CMD_STATS   = "stats";
-        static final String CMD_DUMPSYS = "-a"; // dumpsys.cpp dumps services with "-a" as arguments
         static final String CMD_DEFAULT = CMD_STATS;
 
         @Override
@@ -239,8 +203,6 @@
                 case CMD_FLUSH:
                     cmdFlush(fd, pw, args);
                     return;
-                case CMD_DUMPSYS:
-                    // Fallthrough to CMD_LIST when dumpsys.cpp dumps services states (bug reports)
                 case CMD_LIST:
                     cmdList(fd, pw, args);
                     return;
@@ -264,20 +226,4 @@
             getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
         }
     };
-
-    private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
-        int size = Settings.Global.getInt(ctx.getContentResolver(),
-                Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
-        if (size <= 0) {
-            return DEFAULT_BUFFER_SIZE;
-        }
-        return Math.min(size, MAXIMUM_BUFFER_SIZE);
-    };
-
-    private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
-        ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
-        // one token every minute, 50 tokens max: burst of ~50 events every hour.
-        map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
-        return map;
-    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index f8638c5..4b175d7 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -24,7 +24,6 @@
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
-import android.os.RemoteException;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -45,7 +44,7 @@
     public static final String SERVICE_NAME = "netd_listener";
 
     private static final String TAG = NetdEventListenerService.class.getSimpleName();
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
     // TODO: read this constant from system property
@@ -87,7 +86,7 @@
             byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount);
             int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount);
             mMetricsLog.log(new DnsEvent(mNetId, eventTypes, returnCodes, latenciesMs));
-            maybeLog("Logging %d results for netId %d", mEventCount, mNetId);
+            maybeLog(String.format("Logging %d results for netId %d", mEventCount, mNetId));
             mEventCount = 0;
         }
 
@@ -137,9 +136,9 @@
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
     public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
-            String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
-            throws RemoteException {
-        maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
+            String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
+        maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)",
+                netId, eventType, returnCode, latencyMs));
 
         DnsEventBatch batch = mEventBatches.get(netId);
         if (batch == null) {
@@ -152,9 +151,9 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
-    public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
-            int uid) throws RemoteException {
-        maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
+    public synchronized void onConnectEvent(int netId, int latencyMs, String ipAddr, int port,
+            int uid) {
+        maybeVerboseLog(String.format("onConnectEvent(%d, %d)", netId, latencyMs));
     }
 
     public synchronized void dump(PrintWriter writer) {
@@ -167,11 +166,11 @@
         pw.decreaseIndent();
     }
 
-    private static void maybeLog(String s, Object... args) {
-        if (DBG) Log.d(TAG, String.format(s, args));
+    private static void maybeLog(String s) {
+        if (DBG) Log.d(TAG, s);
     }
 
-    private static void maybeVerboseLog(String s, Object... args) {
-        if (VDBG) Log.d(TAG, String.format(s, args));
+    private static void maybeVerboseLog(String s) {
+        if (VDBG) Log.d(TAG, s);
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index ea2cf5f..6eb89fa 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -96,24 +96,6 @@
     private static final int SOCKET_TIMEOUT_MS = 10000;
     private static final int PROBE_TIMEOUT_MS  = 3000;
 
-    static enum EvaluationResult {
-        VALIDATED(true),
-        CAPTIVE_PORTAL(false);
-        final boolean isValidated;
-        EvaluationResult(boolean isValidated) {
-            this.isValidated = isValidated;
-        }
-    }
-
-    static enum ValidationStage {
-        FIRST_VALIDATION(true),
-        REVALIDATION(false);
-        final boolean isFirstValidation;
-        ValidationStage(boolean isFirstValidation) {
-            this.isFirstValidation = isFirstValidation;
-        }
-    }
-
     public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
             "android.net.conn.NETWORK_CONDITIONS_MEASURED";
     public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
@@ -229,12 +211,8 @@
     private final NetworkRequest mDefaultRequest;
     private final IpConnectivityLog mMetricsLog;
 
-    @VisibleForTesting
-    protected boolean mIsCaptivePortalCheckEnabled;
-
+    private boolean mIsCaptivePortalCheckEnabled;
     private boolean mUseHttps;
-    // The total number of captive portal detection attempts for this NetworkMonitor instance.
-    private int mValidations = 0;
 
     // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
     private boolean mUserDoesNotWant = false;
@@ -287,8 +265,7 @@
         setInitialState(mDefaultState);
 
         mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT)
-                != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
+                Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
         mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
 
@@ -309,10 +286,6 @@
         return validationLogs.readOnlyLocalLog();
     }
 
-    private ValidationStage validationStage() {
-        return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
-    }
-
     // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
     // does not entail any real state (hence no enter() or exit() routines).
     private class DefaultState extends State {
@@ -389,11 +362,9 @@
     private class ValidatedState extends State {
         @Override
         public void enter() {
-            maybeLogEvaluationResult(
-                    networkEventType(validationStage(), EvaluationResult.VALIDATED));
+            maybeLogEvaluationResult(NetworkEvent.NETWORK_VALIDATED);
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
-            mValidations++;
         }
 
         @Override
@@ -609,8 +580,7 @@
 
         @Override
         public void enter() {
-            maybeLogEvaluationResult(
-                    networkEventType(validationStage(), EvaluationResult.CAPTIVE_PORTAL));
+            maybeLogEvaluationResult(NetworkEvent.NETWORK_CAPTIVE_PORTAL_FOUND);
             // Don't annoy user with sign-in notifications.
             if (mDontDisplaySigninNotification) return;
             // Create a CustomIntentReceiver that sends us a
@@ -630,7 +600,6 @@
             // Retest for captive portal occasionally.
             sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
                     CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
-            mValidations++;
         }
 
         @Override
@@ -663,10 +632,7 @@
 
     @VisibleForTesting
     protected CaptivePortalProbeResult isCaptivePortal() {
-        if (!mIsCaptivePortalCheckEnabled) {
-            validationLog("Validation disabled.");
-            return new CaptivePortalProbeResult(204);
-        }
+        if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204);
 
         URL pacUrl = null, httpsUrl = null, httpUrl = null, fallbackUrl = null;
 
@@ -706,13 +672,48 @@
 
         long startTime = SystemClock.elapsedRealtime();
 
-        final CaptivePortalProbeResult result;
+        // Pre-resolve the captive portal server host so we can log it.
+        // Only do this if HttpURLConnection is about to, to avoid any potentially
+        // unnecessary resolution.
+        String hostToResolve = null;
         if (pacUrl != null) {
-            result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC);
-        } else if (mUseHttps) {
-            result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl, fallbackUrl);
+            hostToResolve = pacUrl.getHost();
+        } else if (proxyInfo != null) {
+            hostToResolve = proxyInfo.getHost();
         } else {
-            result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP);
+            hostToResolve = httpUrl.getHost();
+        }
+
+        if (!TextUtils.isEmpty(hostToResolve)) {
+            String probeName = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS);
+            final Stopwatch dnsTimer = new Stopwatch().start();
+            int dnsResult;
+            long dnsLatency;
+            try {
+                InetAddress[] addresses = mNetworkAgentInfo.network.getAllByName(hostToResolve);
+                dnsResult = ValidationProbeEvent.DNS_SUCCESS;
+                dnsLatency = dnsTimer.stop();
+                final StringBuffer connectInfo = new StringBuffer(", " + hostToResolve + "=");
+                for (InetAddress address : addresses) {
+                    connectInfo.append(address.getHostAddress());
+                    if (address != addresses[addresses.length-1]) connectInfo.append(",");
+                }
+                validationLog(probeName + " OK " + dnsLatency + "ms" + connectInfo);
+            } catch (UnknownHostException e) {
+                dnsResult = ValidationProbeEvent.DNS_FAILURE;
+                dnsLatency = dnsTimer.stop();
+                validationLog(probeName + " FAIL " + dnsLatency + "ms, " + hostToResolve);
+            }
+            logValidationProbe(dnsLatency, ValidationProbeEvent.PROBE_DNS, dnsResult);
+        }
+
+        CaptivePortalProbeResult result;
+        if (pacUrl != null) {
+            result = sendHttpProbe(pacUrl, ValidationProbeEvent.PROBE_PAC);
+        } else if (mUseHttps) {
+            result = sendParallelHttpProbes(httpsUrl, httpUrl, fallbackUrl);
+        } else {
+            result = sendHttpProbe(httpUrl, ValidationProbeEvent.PROBE_HTTP);
         }
 
         long endTime = SystemClock.elapsedRealtime();
@@ -725,50 +726,8 @@
     }
 
     /**
-     * Do a DNS resolution and URL fetch on a known web server to see if we get the data we expect.
-     * @return a CaptivePortalProbeResult inferred from the HTTP response.
-     */
-    private CaptivePortalProbeResult sendDnsAndHttpProbes(ProxyInfo proxy, URL url, int probeType) {
-        // Pre-resolve the captive portal server host so we can log it.
-        // Only do this if HttpURLConnection is about to, to avoid any potentially
-        // unnecessary resolution.
-        final String host = (proxy != null) ? proxy.getHost() : url.getHost();
-        sendDnsProbe(host);
-        return sendHttpProbe(url, probeType);
-    }
-
-    /** Do a DNS resolution of the given server. */
-    private void sendDnsProbe(String host) {
-        if (TextUtils.isEmpty(host)) {
-            return;
-        }
-
-        final String name = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS);
-        final Stopwatch watch = new Stopwatch().start();
-        int result;
-        String connectInfo;
-        try {
-            InetAddress[] addresses = mNetworkAgentInfo.network.getAllByName(host);
-            result = ValidationProbeEvent.DNS_SUCCESS;
-            StringBuffer buffer = new StringBuffer(host).append("=");
-            for (InetAddress address : addresses) {
-                buffer.append(address.getHostAddress());
-                if (address != addresses[addresses.length-1]) buffer.append(",");
-            }
-            connectInfo = buffer.toString();
-        } catch (UnknownHostException e) {
-            result = ValidationProbeEvent.DNS_FAILURE;
-            connectInfo = host;
-        }
-        final long latency = watch.stop();
-        String resultString = (ValidationProbeEvent.DNS_SUCCESS == result) ? "OK" : "FAIL";
-        validationLog(String.format("%s %s %dms, %s", name, resultString, latency, connectInfo));
-        logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result);
-    }
-
-    /**
-     * Do a URL fetch on a known web server to see if we get the data we expect.
-     * @return a CaptivePortalProbeResult inferred from the HTTP response.
+     * Do a URL fetch on a known server to see if we get the data we expect.
+     * Returns HTTP response code.
      */
     @VisibleForTesting
     protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType) {
@@ -835,7 +794,7 @@
     }
 
     private CaptivePortalProbeResult sendParallelHttpProbes(
-            ProxyInfo proxy, URL httpsUrl, URL httpUrl, URL fallbackUrl) {
+            URL httpsUrl, URL httpUrl, URL fallbackUrl) {
         // Number of probes to wait for. If a probe completes with a conclusive answer
         // it shortcuts the latch immediately by forcing the count to 0.
         final CountDownLatch latch = new CountDownLatch(2);
@@ -855,10 +814,9 @@
             @Override
             public void run() {
                 if (mIsHttps) {
-                    mResult =
-                            sendDnsAndHttpProbes(proxy, httpsUrl, ValidationProbeEvent.PROBE_HTTPS);
+                    mResult = sendHttpProbe(httpsUrl, ValidationProbeEvent.PROBE_HTTPS);
                 } else {
-                    mResult = sendDnsAndHttpProbes(proxy, httpUrl, ValidationProbeEvent.PROBE_HTTP);
+                    mResult = sendHttpProbe(httpUrl, ValidationProbeEvent.PROBE_HTTP);
                 }
                 if ((mIsHttps && mResult.isSuccessful()) || (!mIsHttps && mResult.isPortal())) {
                     // Stop waiting immediately if https succeeds or if http finds a portal.
@@ -1009,22 +967,6 @@
         mMetricsLog.log(new NetworkEvent(mNetId, evtype));
     }
 
-    private int networkEventType(ValidationStage s, EvaluationResult r) {
-        if (s.isFirstValidation) {
-            if (r.isValidated) {
-                return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
-            } else {
-                return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
-            }
-        } else {
-            if (r.isValidated) {
-                return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
-            } else {
-                return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
-            }
-        }
-    }
-
     private void maybeLogEvaluationResult(int evtype) {
         if (mEvaluationTimer.isRunning()) {
             mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop()));
@@ -1033,8 +975,6 @@
     }
 
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
-        probeType =
-                ValidationProbeEvent.makeProbeType(probeType, validationStage().isFirstValidation);
         mMetricsLog.log(new ValidationProbeEvent(mNetId, durationMs, probeType, probeResult));
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6d96a10..0beb227 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -71,7 +71,6 @@
 import com.android.internal.util.StateMachine;
 import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
-import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -1940,8 +1939,7 @@
     private void trackNewTetherableInterface(String iface, int interfaceType) {
         TetherState tetherState;
         tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper,
-                interfaceType, mNMService, mStatsService, this,
-                new IPv6TetheringInterfaceServices(iface, mNMService)));
+                interfaceType, mNMService, mStatsService, this));
         mTetherStates.put(iface, tetherState);
         tetherState.mStateMachine.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index afc6247..ede3bda 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -27,8 +27,6 @@
 import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -78,7 +76,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.LegacyVpnInfo;
@@ -244,14 +241,12 @@
     /**
      * Update current state, dispaching event to listeners.
      */
-    @VisibleForTesting
-    protected void updateState(DetailedState detailedState, String reason) {
+    private void updateState(DetailedState detailedState, String reason) {
         if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
         mNetworkInfo.setDetailedState(detailedState, reason, null);
         if (mNetworkAgent != null) {
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
         }
-        updateAlwaysOnNotification(detailedState);
     }
 
     /**
@@ -285,10 +280,7 @@
         }
 
         mLockdown = (mAlwaysOn && lockdown);
-        if (isCurrentPreparedPackage(packageName)) {
-            updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
-        } else {
-            // Prepare this app. The notification will update as a side-effect of updateState().
+        if (!isCurrentPreparedPackage(packageName)) {
             prepareInternal(packageName);
         }
         maybeRegisterPackageChangeReceiverLocked(packageName);
@@ -690,19 +682,22 @@
         }
     }
 
-    private void agentDisconnect(NetworkAgent networkAgent) {
+    private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
+        networkInfo.setIsAvailable(false);
+        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
         if (networkAgent != null) {
-            NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
-            networkInfo.setIsAvailable(false);
-            networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
             networkAgent.sendNetworkInfo(networkInfo);
         }
     }
 
+    private void agentDisconnect(NetworkAgent networkAgent) {
+        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
+        agentDisconnect(networkInfo, networkAgent);
+    }
+
     private void agentDisconnect() {
         if (mNetworkInfo.isConnected()) {
-            mNetworkInfo.setIsAvailable(false);
-            updateState(DetailedState.DISCONNECTED, "agentDisconnect");
+            agentDisconnect(mNetworkInfo, mNetworkAgent);
             mNetworkAgent = null;
         }
     }
@@ -1255,43 +1250,6 @@
         }
     }
 
-    private void updateAlwaysOnNotification(DetailedState networkState) {
-        final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
-        updateAlwaysOnNotificationInternal(visible);
-    }
-
-    @VisibleForTesting
-    protected void updateAlwaysOnNotificationInternal(boolean visible) {
-        final UserHandle user = UserHandle.of(mUserHandle);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            final NotificationManager notificationManager = NotificationManager.from(mContext);
-            if (!visible) {
-                notificationManager.cancelAsUser(TAG, 0, user);
-                return;
-            }
-            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
-            final PendingIntent configIntent = PendingIntent.getActivityAsUser(
-                    mContext, /* request */ 0, intent,
-                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
-                    null, user);
-            final Notification.Builder builder = new Notification.Builder(mContext)
-                    .setDefaults(0)
-                    .setSmallIcon(R.drawable.vpn_connected)
-                    .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected))
-                    .setContentText(mContext.getString(R.string.vpn_lockdown_config))
-                    .setContentIntent(configIntent)
-                    .setCategory(Notification.CATEGORY_SYSTEM)
-                    .setPriority(Notification.PRIORITY_LOW)
-                    .setVisibility(Notification.VISIBILITY_PUBLIC)
-                    .setOngoing(true)
-                    .setColor(mContext.getColor(R.color.system_notification_accent_color));
-            notificationManager.notifyAsUser(TAG, 0, builder.build(), user);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     private native int jniCreate(int mtu);
     private native String jniGetName(int tun);
     private native int jniSetAddresses(String interfaze, String addresses);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index dec2f77..c2c1a8c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -45,7 +45,7 @@
 /**
  * @hide
  */
-public class IPv6TetheringInterfaceServices {
+class IPv6TetheringInterfaceServices {
     private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
     private static final int RFC7421_IP_PREFIX_LENGTH = 64;
@@ -59,7 +59,7 @@
     private RouterAdvertisementDaemon mRaDaemon;
     private RaParams mLastRaParams;
 
-    public IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
+    IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
         mIfName = ifname;
         mNMService = nms;
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 37221a9..6ca4e27 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -94,14 +94,14 @@
 
     public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
                     INetworkManagementService nMService, INetworkStatsService statsService,
-                    IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
+                    IControlsTethering tetherController) {
         super(ifaceName, looper);
         mNMService = nMService;
         mStatsService = statsService;
         mTetherController = tetherController;
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
-        mIPv6TetherSvc = ipv6Svc;
+        mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService);
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         mInitialState = new InitialState();
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index c48f430..959a823 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -91,19 +91,6 @@
         }
     }
 
-    /** @return whether any {@link NetworkIdentity} in this set is considered metered. */
-    public boolean isAnyMemberMetered() {
-        if (isEmpty()) {
-            return false;
-        }
-        for (NetworkIdentity ident : this) {
-            if (ident.getMetered()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */
     public boolean isAnyMemberRoaming() {
         if (isEmpty()) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c45b416..673dd8f 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -17,8 +17,6 @@
 package com.android.server.net;
 
 import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.NetworkStats.SET_ALL;
@@ -244,7 +242,6 @@
                 entry.uid = key.uid;
                 entry.set = key.set;
                 entry.tag = key.tag;
-                entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
                 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
                 entry.rxBytes = historyEntry.rxBytes;
                 entry.rxPackets = historyEntry.rxPackets;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 5016ec0..a6f9243 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -1013,7 +1013,7 @@
                         permissions.clear();
                     }
                     permissions.add(permissionGrant.name);
-                    grantRuntimePermissionsLPw(pkg, permissions, false,
+                    grantRuntimePermissionsLPw(pkg, permissions,
                             permissionGrant.fixed, userId);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1f83d9e..98249dd1 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -54,6 +54,7 @@
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
     public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+    public static final int FLAG_USE_QUOTA = 1 << 12;
 
     private final boolean mIsolated;
 
@@ -198,15 +199,44 @@
         }
     }
 
-    public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
-            String codePath, PackageStats stats) throws InstallerException {
+    public void getAppSize(String uuid, String[] packageNames, int userId, int flags, int appId,
+            long[] ceDataInodes, String[] codePaths, PackageStats stats)
+            throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
-                    codePath);
+            final long[] res = mInstalld.getAppSize(uuid, packageNames, userId, flags,
+                    appId, ceDataInodes, codePaths);
             stats.codeSize += res[0];
             stats.dataSize += res[1];
             stats.cacheSize += res[2];
+            stats.externalCodeSize += res[3];
+            stats.externalDataSize += res[4];
+            stats.externalCacheSize += res[5];
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public void getUserSize(String uuid, int userId, int flags, int[] appIds, PackageStats stats)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            final long[] res = mInstalld.getUserSize(uuid, userId, flags, appIds);
+            stats.codeSize += res[0];
+            stats.dataSize += res[1];
+            stats.cacheSize += res[2];
+            stats.externalCodeSize += res[3];
+            stats.externalDataSize += res[4];
+            stats.externalCacheSize += res[5];
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public long[] getExternalSize(String uuid, int userId, int flags) throws InstallerException {
+        if (!checkBeforeRemote()) return new long[4];
+        try {
+            return mInstalld.getExternalSize(uuid, userId, flags);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ce5aa8..d79b923 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -370,6 +370,9 @@
     private static final boolean DISABLE_EPHEMERAL_APPS = false;
     private static final boolean HIDE_EPHEMERAL_APIS = true;
 
+    private static final boolean ENABLE_QUOTA =
+            SystemProperties.getBoolean("persist.fw.quota", false);
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -1140,8 +1143,6 @@
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
-    final boolean mPermissionReviewRequired;
-
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
 
@@ -2072,10 +2073,6 @@
         }
 
         mContext = context;
-
-        mPermissionReviewRequired = context.getResources().getBoolean(
-                R.bool.config_permissionReviewRequired);
-
         mFactoryTest = factoryTest;
         mOnlyCore = onlyCore;
         mMetrics = new DisplayMetrics();
@@ -4033,7 +4030,7 @@
             // their permissions as always granted runtime ones since we need
             // to keep the review required permission flag per user while an
             // install permission's state is shared across all users.
-            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
                     && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                     && bp.isRuntime()) {
                 return;
@@ -4144,7 +4141,7 @@
             // their permissions as always granted runtime ones since we need
             // to keep the review required permission flag per user while an
             // install permission's state is shared across all users.
-            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
                     && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                     && bp.isRuntime()) {
                 return;
@@ -10030,8 +10027,7 @@
                     // their permissions as always granted runtime ones since we need
                     // to keep the review required permission flag per user while an
                     // install permission's state is shared across all users.
-                    if (!appSupportsRuntimePermissions && !mPermissionReviewRequired
-                            && !Build.PERMISSIONS_REVIEW_REQUIRED) {
+                    if (!appSupportsRuntimePermissions && !Build.PERMISSIONS_REVIEW_REQUIRED) {
                         // For legacy apps dangerous permissions are install time ones.
                         grant = GRANT_INSTALL;
                     } else if (origPermissions.hasInstallPermission(bp.name)) {
@@ -10117,7 +10113,7 @@
                                             changedRuntimePermissionUserIds, userId);
                                 }
                                 // If the app supports runtime permissions no need for a review.
-                                if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
+                                if (Build.PERMISSIONS_REVIEW_REQUIRED
                                         && appSupportsRuntimePermissions
                                         && (flags & PackageManager
                                                 .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
@@ -10126,8 +10122,7 @@
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
                                 }
-                            } else if ((mPermissionReviewRequired
-                                        || Build.PERMISSIONS_REVIEW_REQUIRED)
+                            } else if (Build.PERMISSIONS_REVIEW_REQUIRED
                                     && !appSupportsRuntimePermissions) {
                                 // For legacy apps that need a permission review, every new
                                 // runtime permission is granted but it is pending a review.
@@ -16722,7 +16717,7 @@
             // If permission review is enabled and this is a legacy app, mark the
             // permission as requiring a review as this is the initial state.
             int flags = 0;
-            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
                     && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
                 flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
             }
@@ -16878,20 +16873,28 @@
                 return false;
             }
         }
+
+        final String[] packageNames = { packageName };
+        final long[] ceDataInodes = { ps.getCeDataInode(userId) };
+        final String[] codePaths = { ps.codePathString };
+
         try {
-            mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
-                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
-                    ps.getCeDataInode(userId), ps.codePathString, stats);
+            mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
+                    ps.appId, ceDataInodes, codePaths, stats);
+
+            // For now, ignore code size of packages on system partition
+            if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+                stats.codeSize = 0;
+            }
+
+            // External clients expect these to be tracked separately
+            stats.dataSize -= stats.cacheSize;
+
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
             return false;
         }
 
-        // For now, ignore code size of packages on system partition
-        if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
-            stats.codeSize = 0;
-        }
-
         return true;
     }
 
@@ -20584,7 +20587,7 @@
         // permissions to keep per user flag state whether review is needed.
         // Hence, if a new user is added we have to propagate dangerous
         // permission grants for these legacy apps.
-        if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
             updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                     | UPDATE_PERMISSIONS_REPLACE_ALL);
         }
@@ -21038,7 +21041,7 @@
         public boolean isPermissionsReviewRequired(String packageName, int userId) {
             synchronized (mPackages) {
                 // If we do not support permission review, done.
-                if (!mPermissionReviewRequired && !Build.PERMISSIONS_REVIEW_REQUIRED) {
+                if (!Build.PERMISSIONS_REVIEW_REQUIRED) {
                     return false;
                 }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index f0c6210..2a525d4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1013,15 +1013,10 @@
             if (attachedTransformation != null) {
                 tmpMatrix.postConcat(attachedTransformation.getMatrix());
             }
+            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
             if (appTransformation != null) {
                 tmpMatrix.postConcat(appTransformation.getMatrix());
             }
-
-            // The translation that applies the position of the window needs to be applied at the
-            // end in case that other translations include scaling. Otherwise the scaling will
-            // affect this translation. But it needs to be set before the screen rotation animation
-            // so the pivot point is at the center of the screen for all windows.
-            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
diff --git a/services/core/proto/ipconnectivity.proto b/services/core/proto/ipconnectivity.proto
index 29b318f..e0d7f09 100644
--- a/services/core/proto/ipconnectivity.proto
+++ b/services/core/proto/ipconnectivity.proto
@@ -53,7 +53,6 @@
 
   // The event type code of the probe, represented by constants defined in
   // android.net.metrics.IpReachabilityEvent.
-  // NUD_FAILED_ORGANIC and PROVISIONING_LOST_ORGANIC recorded since version 1.
   optional int32 event_type = 2;
 };
 
@@ -127,12 +126,11 @@
 
   // Lifetime duration in milliseconds of a DhcpClient state, or transition
   // time in milliseconds between specific pairs of DhcpClient's states.
-  // Only populated since version 1, when state_transition is populated.
+  // Only populated when state_transition is populated.
   optional int32 duration_ms = 4;
 }
 
 // Represents the generation of an Android Packet Filter program.
-// Since version 1.
 message ApfProgramEvent {
   // Lifetime of the program in seconds.
   optional int64 lifetime = 1;
@@ -156,7 +154,6 @@
 
 // Represents Router Advertisement listening statistics for an interface with
 // Android Packet Filter enabled.
-// Since version 1.
 message ApfStatistics {
   // The time interval in milliseconds these stastistics cover.
   optional int64 duration_ms = 1;
@@ -186,7 +183,6 @@
 
 // Represents the reception of a Router Advertisement packet for an interface
 // with Android Packet Filter enabled.
-// Since version 1.
 message RaEvent {
   // All lifetime values are expressed in seconds. The default value for an
   // option lifetime that was not present in the RA option list is -1.
@@ -275,11 +271,4 @@
 
   // The number of events that had to be dropped due to a full buffer.
   optional int32 dropped_events = 2;
-
-  // The version number of the metrics events being collected.
-  //  nyc-dev: not populated, implicitly 0
-  //  nyc-dr1: not populated, implicitly 1 (sailfish and marlin only)
-  //  nyc-mr1: not populated, implicitly 1
-  //  nyc-mr2: 2
-  optional int32 version = 3;
 };
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 83001df..4c75452 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -194,10 +194,8 @@
             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-    private static final int ICMP6_ROUTER_SOLICITATION = 133;
-    private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
-    private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
     private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
+    private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
 
     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
     private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
@@ -285,20 +283,14 @@
         mReceiveThread.start();
     }
 
-    // Returns seconds since device boot.
+    // Returns seconds since Unix Epoch.
+    // TODO: use SystemClock.elapsedRealtime() instead
     private static long curTime() {
-        return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
-    }
-
-    public static class InvalidRaException extends Exception {
-        public InvalidRaException(String m) {
-            super(m);
-        }
+        return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
     }
 
     // A class to hold information about an RA.
-    @VisibleForTesting
-    class Ra {
+    private class Ra {
         // From RFC4861:
         private static final int ICMP6_RA_HEADER_LEN = 16;
         private static final int ICMP6_RA_CHECKSUM_OFFSET =
@@ -370,7 +362,7 @@
             } catch (UnsupportedOperationException e) {
                 // array() failed. Cannot happen, mPacket is array-backed and read-write.
                 return "???";
-            } catch (ClassCastException|UnknownHostException e) {
+            } catch (ClassCastException | UnknownHostException e) {
                 // Cannot happen.
                 return "???";
             }
@@ -380,16 +372,16 @@
         // TODO: Make this static once RA is its own class.
         private void prefixOptionToString(StringBuffer sb, int offset) {
             String prefix = IPv6AddresstoString(offset + 16);
-            int length = getUint8(mPacket, offset + 2);
-            long valid = getUint32(mPacket, offset + 4);
-            long preferred = getUint32(mPacket, offset + 8);
+            int length = uint8(mPacket.get(offset + 2));
+            long valid = mPacket.getInt(offset + 4);
+            long preferred = mPacket.getInt(offset + 8);
             sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred));
         }
 
         private void rdnssOptionToString(StringBuffer sb, int offset) {
-            int optLen = getUint8(mPacket, offset + 1) * 8;
+            int optLen = uint8(mPacket.get(offset + 1)) * 8;
             if (optLen < 24) return;  // Malformed or empty.
-            long lifetime = getUint32(mPacket, offset + 4);
+            long lifetime = uint32(mPacket.getInt(offset + 4));
             int numServers = (optLen - 8) / 16;
             sb.append("DNS ").append(lifetime).append("s");
             for (int server = 0; server < numServers; server++) {
@@ -403,7 +395,7 @@
                 sb.append(String.format("RA %s -> %s %ds ",
                         IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
                         IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
-                        getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)));
+                        uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET))));
                 for (int i: mPrefixOptionOffsets) {
                     prefixOptionToString(sb, i);
                 }
@@ -411,7 +403,7 @@
                     rdnssOptionToString(sb, i);
                 }
                 return sb.toString();
-            } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
+            } catch (BufferUnderflowException | IndexOutOfBoundsException e) {
                 return "<Malformed RA>";
             }
         }
@@ -444,20 +436,16 @@
         // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
         // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
         // specifications.
-        Ra(byte[] packet, int length) throws InvalidRaException {
-            if (length < ICMP6_RA_OPTION_OFFSET) {
-                throw new InvalidRaException("Not an ICMP6 router advertisement");
-            }
-
+        Ra(byte[] packet, int length) {
             mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
             mLastSeen = curTime();
 
             // Sanity check packet in case a packet arrives before we attach RA filter
             // to our packet socket. b/29586253
             if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
-                    getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
-                    getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) {
-                throw new InvalidRaException("Not an ICMP6 router advertisement");
+                    uint8(mPacket.get(IPV6_NEXT_HEADER_OFFSET)) != IPPROTO_ICMPV6 ||
+                    uint8(mPacket.get(ICMP6_TYPE_OFFSET)) != ICMP6_ROUTER_ADVERTISEMENT) {
+                throw new IllegalArgumentException("Not an ICMP6 router advertisement");
             }
 
 
@@ -478,8 +466,8 @@
             mPacket.position(ICMP6_RA_OPTION_OFFSET);
             while (mPacket.hasRemaining()) {
                 final int position = mPacket.position();
-                final int optionType = getUint8(mPacket, position);
-                final int optionLength = getUint8(mPacket, position + 1) * 8;
+                final int optionType = uint8(mPacket.get(position));
+                final int optionLength = uint8(mPacket.get(position + 1)) * 8;
                 long lifetime;
                 switch (optionType) {
                     case ICMP6_PREFIX_OPTION_TYPE:
@@ -523,7 +511,7 @@
                         break;
                 }
                 if (optionLength <= 0) {
-                    throw new InvalidRaException(String.format(
+                    throw new IllegalArgumentException(String.format(
                         "Invalid option length opt=%d len=%d", optionType, optionLength));
                 }
                 mPacket.position(position + optionLength);
@@ -564,10 +552,10 @@
                 final long optionLifetime;
                 switch (lifetimeLength) {
                     case 2:
-                        optionLifetime = getUint16(byteBuffer, offset);
+                        optionLifetime = uint16(byteBuffer.getShort(offset));
                         break;
                     case 4:
-                        optionLifetime = getUint32(byteBuffer, offset);
+                        optionLifetime = uint32(byteBuffer.getInt(offset));
                         break;
                     default:
                         throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
@@ -807,8 +795,6 @@
         //   if it's multicast and we're dropping multicast:
         //     drop
         //   pass
-        // if it's ICMPv6 RS to any:
-        //   drop
         // if it's ICMPv6 NA to ff02::1:
         //   drop
 
@@ -833,12 +819,10 @@
 
         // Add unsolicited multicast neighbor announcements filter
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
+        // If not neighbor announcements, skip unsolicited multicast NA filter
         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
-        // Drop all router solicitations (b/32833400)
-        gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL);
-        // If not neighbor announcements, skip filter.
         gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
-        // If to ff02::1, drop.
+        // If to ff02::1, drop
         // TODO: Drop only if they don't contain the address of on-link neighbours.
         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
         gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
@@ -858,7 +842,6 @@
      * <li>Pass all non-ICMPv6 IPv6 packets,
      * <li>Pass all non-IPv4 and non-IPv6 packets,
      * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
-     * <li>Drop IPv6 ICMPv6 RSs.
      * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
      *     insertion of RA filters here, or if there aren't any, just passes the packets.
      * </ul>
@@ -942,8 +925,8 @@
             // Execution will reach the end of the program if no filters match, which will pass the
             // packet to the AP.
             program = gen.generate();
-        } catch (IllegalInstructionException|IllegalStateException e) {
-            Log.e(TAG, "Failed to generate APF program.", e);
+        } catch (IllegalInstructionException e) {
+            Log.e(TAG, "Program failed to generate: ", e);
             return;
         }
         mLastTimeInstalledProgram = curTime();
@@ -989,8 +972,7 @@
      * if the current APF program should be updated.
      * @return a ProcessRaResult enum describing what action was performed.
      */
-    @VisibleForTesting
-    synchronized ProcessRaResult processRa(byte[] packet, int length) {
+    private synchronized ProcessRaResult processRa(byte[] packet, int length) {
         if (VDBG) hexDump("Read packet = ", packet, length);
 
         // Have we seen this RA before?
@@ -1029,7 +1011,7 @@
         try {
             ra = new Ra(packet, length);
         } catch (Exception e) {
-            Log.e(TAG, "Error parsing RA", e);
+            Log.e(TAG, "Error parsing RA: " + e);
             return ProcessRaResult.PARSE_ERROR;
         }
         // Ignore 0 lifetime RAs.
@@ -1168,11 +1150,7 @@
         return i & 0xffffffffL;
     }
 
-    private static int getUint8(ByteBuffer buffer, int position) {
-        return uint8(buffer.get(position));
-    }
-
-    private static int getUint16(ByteBuffer buffer, int position) {
+    private static long getUint16(ByteBuffer buffer, int position) {
         return uint16(buffer.getShort(position));
     }
 
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index a883e28..a6bb40c 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -50,9 +50,9 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -163,7 +163,8 @@
     private Map<InetAddress, Short> mIpWatchList = new HashMap<>();
     @GuardedBy("mLock")
     private int mIpWatchListVersion;
-    private volatile boolean mRunning;
+    @GuardedBy("mLock")
+    private boolean mRunning;
     // Time in milliseconds of the last forced probe request.
     private volatile long mLastProbeTimeMs;
 
@@ -245,7 +246,7 @@
     }
 
     public void stop() {
-        mRunning = false;
+        synchronized (mLock) { mRunning = false; }
         clearLinkProperties();
         mNetlinkSocketObserver.clearNetlinkSocket();
     }
@@ -280,6 +281,12 @@
         }
     }
 
+    private boolean stillRunning() {
+        synchronized (mLock) {
+            return mRunning;
+        }
+    }
+
     private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
         for (RouteInfo route : routes) {
             if (!route.hasGateway() && route.matches(ip)) {
@@ -383,12 +390,12 @@
     }
 
     public void probeAll() {
-        final List<InetAddress> ipProbeList;
+        Set<InetAddress> ipProbeList = new HashSet<InetAddress>();
         synchronized (mLock) {
-            ipProbeList = new ArrayList<>(mIpWatchList.keySet());
+            ipProbeList.addAll(mIpWatchList.keySet());
         }
 
-        if (!ipProbeList.isEmpty() && mRunning) {
+        if (!ipProbeList.isEmpty() && stillRunning()) {
             // Keep the CPU awake long enough to allow all ARP/ND
             // probes a reasonable chance at success. See b/23197666.
             //
@@ -399,7 +406,7 @@
         }
 
         for (InetAddress target : ipProbeList) {
-            if (!mRunning) {
+            if (!stillRunning()) {
                 break;
             }
             final int returnValue = probeNeighbor(mInterfaceIndex, target);
@@ -444,21 +451,21 @@
         @Override
         public void run() {
             if (VDBG) { Log.d(TAG, "Starting observing thread."); }
-            mRunning = true;
+            synchronized (mLock) { mRunning = true; }
 
             try {
                 setupNetlinkSocket();
             } catch (ErrnoException | SocketException e) {
                 Log.e(TAG, "Failed to suitably initialize a netlink socket", e);
-                mRunning = false;
+                synchronized (mLock) { mRunning = false; }
             }
 
-            while (mRunning) {
-                final ByteBuffer byteBuffer;
+            ByteBuffer byteBuffer;
+            while (stillRunning()) {
                 try {
                     byteBuffer = recvKernelReply();
                 } catch (ErrnoException e) {
-                    if (mRunning) { Log.w(TAG, "ErrnoException: ", e); }
+                    if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); }
                     break;
                 }
                 final long whenMs = SystemClock.elapsedRealtime();
@@ -470,7 +477,7 @@
 
             clearNetlinkSocket();
 
-            mRunning = false; // Not a no-op when ErrnoException happened.
+            synchronized (mLock) { mRunning = false; }
             if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
         }
 
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 2187c57..3183930 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -12,12 +12,12 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    easymocklib \
     frameworks-base-testutils \
     services.core \
     services.devicepolicy \
     services.net \
     services.usage \
+    easymocklib \
     guava \
     android-support-test \
     mockito-target \
diff --git a/tests/net/jni/UidRangeTest.cpp b/services/tests/servicestests/jni/UidRangeTest.cpp
similarity index 100%
rename from tests/net/jni/UidRangeTest.cpp
rename to services/tests/servicestests/jni/UidRangeTest.cpp
diff --git a/tests/net/jni/UidRangeTest.h b/services/tests/servicestests/jni/UidRangeTest.h
similarity index 100%
rename from tests/net/jni/UidRangeTest.h
rename to services/tests/servicestests/jni/UidRangeTest.h
diff --git a/tests/net/jni/apf_jni.cpp b/services/tests/servicestests/jni/apf_jni.cpp
similarity index 100%
rename from tests/net/jni/apf_jni.cpp
rename to services/tests/servicestests/jni/apf_jni.cpp
diff --git a/tests/net/res/raw/apf.pcap b/services/tests/servicestests/res/raw/apf.pcap
similarity index 100%
rename from tests/net/res/raw/apf.pcap
rename to services/tests/servicestests/res/raw/apf.pcap
Binary files differ
diff --git a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java b/services/tests/servicestests/src/android/net/ConnectivityMetricsLoggerTest.java
similarity index 97%
rename from tests/net/java/android/net/ConnectivityMetricsLoggerTest.java
rename to services/tests/servicestests/src/android/net/ConnectivityMetricsLoggerTest.java
index f896030..6d42cce 100644
--- a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/android/net/ConnectivityMetricsLoggerTest.java
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
 import java.util.List;
 import junit.framework.TestCase;
 import org.mockito.ArgumentCaptor;
@@ -50,7 +49,6 @@
         mLog = new ConnectivityMetricsLogger(mService);
     }
 
-    @SmallTest
     public void testLogEvents() throws Exception {
         mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
         mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
@@ -62,7 +60,6 @@
         assertEventsEqual(expectedEvent(3), gotEvents.get(2));
     }
 
-    @SmallTest
     public void testLogEventTriggerThrottling() throws Exception {
         when(mService.logEvent(any())).thenReturn(1234L);
 
@@ -73,7 +70,6 @@
         assertEventsEqual(expectedEvent(1), gotEvents.get(0));
     }
 
-    @SmallTest
     public void testLogEventFails() throws Exception {
         when(mService.logEvent(any())).thenReturn(-1L); // Error.
 
@@ -84,7 +80,6 @@
         assertEventsEqual(expectedEvent(1), gotEvents.get(0));
     }
 
-    @SmallTest
     public void testLogEventWhenThrottling() throws Exception {
         when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled
 
@@ -97,7 +92,6 @@
         assertEventsEqual(expectedEvent(1), gotEvents.get(0));
     }
 
-    @SmallTest
     public void testLogEventRecoverFromThrottling() throws Exception {
         final long throttleTimeout = System.currentTimeMillis() + 10;
         when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);
diff --git a/tests/net/java/android/net/UidRangeTest.java b/services/tests/servicestests/src/android/net/UidRangeTest.java
similarity index 98%
rename from tests/net/java/android/net/UidRangeTest.java
rename to services/tests/servicestests/src/android/net/UidRangeTest.java
index 0a56e1b..221fe0f 100644
--- a/tests/net/java/android/net/UidRangeTest.java
+++ b/services/tests/servicestests/src/android/net/UidRangeTest.java
@@ -26,7 +26,7 @@
 public class UidRangeTest extends TestCase {
 
     static {
-        System.loadLibrary("frameworksnettestsjni");
+        System.loadLibrary("servicestestsjni");
     }
 
     private static native byte[] readAndWriteNative(byte[] inParcel);
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
similarity index 95%
rename from tests/net/java/android/net/apf/ApfTest.java
rename to services/tests/servicestests/src/android/net/apf/ApfTest.java
index ff61754..f7c61d1 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -16,6 +16,10 @@
 
 package android.net.apf;
 
+import static android.system.OsConstants.*;
+
+import com.android.frameworks.servicestests.R;
+
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
@@ -32,11 +36,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import static android.system.OsConstants.*;
-
-import com.android.frameworks.tests.net.R;
-import com.android.internal.util.HexDump;
+import android.test.suitebuilder.annotation.LargeTest;
 
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -54,7 +54,6 @@
 import java.net.NetworkInterface;
 import java.nio.ByteBuffer;
 import java.util.List;
-import java.util.Random;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -75,7 +74,7 @@
         super.setUp();
         MockitoAnnotations.initMocks(this);
         // Load up native shared library containing APF interpreter exposed via JNI.
-        System.loadLibrary("frameworksnettestsjni");
+        System.loadLibrary("servicestestsjni");
     }
 
     // Expected return codes from APF interpreter.
@@ -154,7 +153,7 @@
      * generating bytecode for that program and running it through the
      * interpreter to verify it functions correctly.
      */
-    @SmallTest
+    @LargeTest
     public void testApfInstructions() throws IllegalInstructionException {
         // Empty program should pass because having the program counter reach the
         // location immediately after the program indicates the packet should be
@@ -562,7 +561,7 @@
      * Generate some BPF programs, translate them to APF, then run APF and BPF programs
      * over packet traces and verify both programs filter out the same packets.
      */
-    @SmallTest
+    @LargeTest
     public void testApfAgainstBpf() throws Exception {
         String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
                 "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
@@ -661,13 +660,9 @@
     // The IPv6 all nodes address ff02::1
     private static final byte[] IPV6_ALL_NODES_ADDRESS =
             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
-    private static final byte[] IPV6_ALL_ROUTERS_ADDRESS =
-            { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-    private static final int ICMP6_ROUTER_SOLICITATION = 133;
     private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
-    private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
     private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
 
     private static final int ICMP6_RA_HEADER_LEN = 16;
@@ -724,7 +719,7 @@
     private static final byte[] ANOTHER_IPV4_ADDR        = {10, 0, 0, 2};
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
-    @SmallTest
+    @LargeTest
     public void testApfFilterIPv4() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
@@ -779,7 +774,7 @@
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @LargeTest
     public void testApfFilterIPv6() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
@@ -802,16 +797,10 @@
         put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS);
         assertDrop(program, packet.array());
 
-        // Verify ICMPv6 RS to any is dropped
-        packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_SOLICITATION);
-        assertDrop(program, packet.array());
-        put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_ROUTERS_ADDRESS);
-        assertDrop(program, packet.array());
-
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @LargeTest
     public void testApfFilterMulticast() throws Exception {
         final byte[] unicastIpv4Addr   = {(byte)192,0,2,63};
         final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
@@ -921,7 +910,7 @@
         assertDrop(program, garpReply());
     }
 
-    @SmallTest
+    @LargeTest
     public void testApfFilterArp() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
@@ -1040,7 +1029,7 @@
         ipManagerCallback.assertNoProgramUpdate();
     }
 
-    @SmallTest
+    @LargeTest
     public void testApfFilterRa() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
@@ -1157,41 +1146,6 @@
         buffer.position(original);
     }
 
-    @SmallTest
-    public void testRaParsing() throws Exception {
-        final int maxRandomPacketSize = 512;
-        final Random r = new Random();
-        MockIpManagerCallback cb = new MockIpManagerCallback();
-        TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
-        for (int i = 0; i < 1000; i++) {
-            byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
-            r.nextBytes(packet);
-            try {
-                apfFilter.new Ra(packet, packet.length);
-            } catch (ApfFilter.InvalidRaException e) {
-            } catch (Exception e) {
-                throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
-            }
-        }
-    }
-
-    @SmallTest
-    public void testRaProcessing() throws Exception {
-        final int maxRandomPacketSize = 512;
-        final Random r = new Random();
-        MockIpManagerCallback cb = new MockIpManagerCallback();
-        TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
-        for (int i = 0; i < 1000; i++) {
-            byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
-            r.nextBytes(packet);
-            try {
-                apfFilter.processRa(packet, packet.length);
-            } catch (Exception e) {
-                throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
-            }
-        }
-    }
-
     /**
      * Call the APF interpreter the run {@code program} on {@code packet} pretending the
      * filter was installed {@code filter_age} seconds ago.
@@ -1213,7 +1167,6 @@
     private native static boolean compareBpfApf(String filter, String pcap_filename,
             byte[] apf_program);
 
-    @SmallTest
     public void testBytesToInt() {
         assertEquals(0x00000000, ApfFilter.bytesToInt(IPV4_ANY_HOST_ADDR));
         assertEquals(0xffffffff, ApfFilter.bytesToInt(IPV4_BROADCAST_ADDRESS));
diff --git a/tests/net/java/android/net/apf/Bpf2Apf.java b/services/tests/servicestests/src/android/net/apf/Bpf2Apf.java
similarity index 100%
rename from tests/net/java/android/net/apf/Bpf2Apf.java
rename to services/tests/servicestests/src/android/net/apf/Bpf2Apf.java
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
similarity index 99%
rename from tests/net/java/android/net/dhcp/DhcpPacketTest.java
rename to services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index d79c312..bc8baa1 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -473,7 +473,6 @@
         assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
     }
 
-    @SmallTest
     public void testTruncatedOfferPackets() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -507,7 +506,6 @@
         }
     }
 
-    @SmallTest
     public void testRandomPackets() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
diff --git a/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
similarity index 97%
rename from tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
index 5deba27..e677475 100644
--- a/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java
+++ b/services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
@@ -24,7 +24,6 @@
 import android.net.netlink.NetlinkErrorMessage;
 import android.net.netlink.NetlinkMessage;
 import android.net.netlink.StructNlMsgErr;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -54,7 +53,6 @@
     public static final byte[] NLM_ERROR_OK =
             HexEncoding.decode(NLM_ERROR_OK_HEX.toCharArray(), false);
 
-    @SmallTest
     public void testParseNlmErrorOk() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(NLM_ERROR_OK);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
diff --git a/tests/net/java/android/net/netlink/NetlinkSocketTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
similarity index 97%
rename from tests/net/java/android/net/netlink/NetlinkSocketTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
index 78b3b70..c599fe3 100644
--- a/tests/net/java/android/net/netlink/NetlinkSocketTest.java
+++ b/services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
@@ -20,7 +20,6 @@
 import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
 import android.net.netlink.StructNlMsgHdr;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
 import android.system.OsConstants;
@@ -34,7 +33,6 @@
 public class NetlinkSocketTest extends TestCase {
     private final String TAG = "NetlinkSocketTest";
 
-    @SmallTest
     public void testBasicWorkingGetNeighborsQuery() throws Exception {
         NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
         assertNotNull(s);
@@ -93,7 +91,6 @@
         s.close();
     }
 
-    @SmallTest
     public void testRepeatedCloseCallsAreQuiet() throws Exception {
         // Create a working NetlinkSocket.
         NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
diff --git a/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java b/services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
similarity index 98%
rename from tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
index 029758e..19ee000 100644
--- a/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -21,13 +21,12 @@
 import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
 import android.net.netlink.StructNlMsgHdr;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.system.OsConstants;
 import android.util.Log;
 import libcore.util.HexEncoding;
 
-import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -136,7 +135,6 @@
     public static final byte[] RTM_GETNEIGH_RESPONSE =
             HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
 
-    @SmallTest
     public void testParseRtmDelNeigh() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -163,7 +161,6 @@
         assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
     }
 
-    @SmallTest
     public void testParseRtmNewNeigh() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -190,7 +187,6 @@
         assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
     }
 
-    @SmallTest
     public void testParseRtmGetNeighResponse() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -215,7 +211,6 @@
         assertEquals(14, messageCount);
     }
 
-    @SmallTest
     public void testCreateRtmNewNeighMessage() {
         final int seqNo = 2635;
         final int ifIndex = 14;
diff --git a/tests/net/java/android/net/util/IpUtilsTest.java b/services/tests/servicestests/src/android/net/util/IpUtilsTest.java
similarity index 100%
rename from tests/net/java/android/net/util/IpUtilsTest.java
rename to services/tests/servicestests/src/android/net/util/IpUtilsTest.java
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
similarity index 98%
rename from tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java
rename to services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
index 8ca849b..808f4dd 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.util.test;
+package com.android.internal.util;
 
 import android.net.Uri;
 import android.os.Bundle;
diff --git a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
similarity index 97%
rename from core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java
rename to services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
index f2be109..05de0a5 100644
--- a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.util.test;
+package com.android.internal.util;
 
 import android.content.ContentResolver;
 import android.database.ContentObserver;
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
similarity index 89%
rename from tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
rename to services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
index 27b7419..13657ab 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.util.test;
+package com.android.server;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -25,12 +25,13 @@
 import android.os.Handler;
 import android.os.UserHandle;
 
-import java.util.ArrayList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AbstractFuture;
+
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -41,17 +42,9 @@
 public class BroadcastInterceptingContext extends ContextWrapper {
     private static final String TAG = "WatchingContext";
 
-    private final List<BroadcastInterceptor> mInterceptors = new ArrayList<>();
+    private final List<BroadcastInterceptor> mInterceptors = Lists.newArrayList();
 
-    public abstract class FutureIntent extends FutureTask<Intent> {
-        public FutureIntent() {
-            super(
-                () -> { throw new IllegalStateException("Cannot happen"); }
-            );
-        }
-    }
-
-    public class BroadcastInterceptor extends FutureIntent {
+    public class BroadcastInterceptor extends AbstractFuture<Intent> {
         private final BroadcastReceiver mReceiver;
         private final IntentFilter mFilter;
 
@@ -89,11 +82,11 @@
         super(base);
     }
 
-    public FutureIntent nextBroadcastIntent(String action) {
+    public Future<Intent> nextBroadcastIntent(String action) {
         return nextBroadcastIntent(new IntentFilter(action));
     }
 
-    public FutureIntent nextBroadcastIntent(IntentFilter filter) {
+    public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
         final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
         synchronized (mInterceptors) {
             mInterceptors.add(interceptor);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
similarity index 90%
rename from tests/net/java/com/android/server/ConnectivityServiceTest.java
rename to services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index d62c30d..4af1cf1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -68,15 +68,14 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
-import android.test.FlakyTest;
 import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
+import com.android.internal.util.FakeSettingsProvider;
 import com.android.internal.util.WakeupMessage;
-import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
@@ -90,7 +89,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BooleanSupplier;
 
 /**
  * Tests for {@link ConnectivityService}.
@@ -195,7 +193,6 @@
     }
 
     // Tests that IdleableHandlerThread works as expected.
-    @SmallTest
     public void testIdleableHandlerThread() {
         final int attempts = 50;  // Causes the test to take about 200ms on bullhead-eng.
 
@@ -218,21 +215,8 @@
             mService.waitForIdle();
             assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength());
         }
-    }
-
-    @SmallTest
-    @FlakyTest(tolerance = 3)
-    public void testNotWaitingForIdleCausesRaceConditions() {
-        // Bring up a network that we can use to send messages to ConnectivityService.
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(false);
-        waitFor(cv);
-        Network n = mWiFiNetworkAgent.getNetwork();
-        assertNotNull(n);
 
         // Ensure that not calling waitForIdle causes a race condition.
-        final int attempts = 50;  // Causes the test to take about 200ms on bullhead-eng.
         for (int i = 0; i < attempts; i++) {
             mWiFiNetworkAgent.setSignalStrength(i);
             if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) {
@@ -252,7 +236,6 @@
         private final IdleableHandlerThread mHandlerThread;
         private final ConditionVariable mDisconnected = new ConditionVariable();
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
-        private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
         private int mScore;
         private NetworkAgent mNetworkAgent;
         private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
@@ -308,11 +291,6 @@
                     mRedirectUrl = redirectUrl;
                     mNetworkStatusReceived.open();
                 }
-
-                @Override
-                protected void preventAutomaticReconnect() {
-                    mPreventReconnectReceived.open();
-                }
             };
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
@@ -397,6 +375,11 @@
             mWrappedNetworkMonitor.gen204ProbeResult = 200;
             mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
             connect(false);
+            waitFor(new Criteria() { public boolean get() {
+                NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
+                return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
+            mWrappedNetworkMonitor.gen204ProbeResult = 500;
+            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null;
         }
 
         public void disconnect() {
@@ -408,10 +391,6 @@
             return new Network(mNetworkAgent.netId);
         }
 
-        public ConditionVariable getPreventReconnectReceived() {
-            return mPreventReconnectReceived;
-        }
-
         public ConditionVariable getDisconnectedCV() {
             return mDisconnected;
         }
@@ -618,13 +597,12 @@
 
         @Override
         protected CaptivePortalProbeResult isCaptivePortal() {
-            if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
             return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
         }
     }
 
     private class WrappedAvoidBadWifiTracker extends AvoidBadWifiTracker {
-        public volatile boolean configRestrictsAvoidBadWifi;
+        public boolean configRestrictsAvoidBadWifi;
 
         public WrappedAvoidBadWifiTracker(Context c, Handler h, Runnable r) {
             super(c, h, r);
@@ -729,7 +707,10 @@
     static private void waitFor(Criteria criteria) {
         int delays = 0;
         while (!criteria.get()) {
-            sleepFor(50);
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+            }
             if (++delays == 10) fail();
         }
     }
@@ -762,9 +743,6 @@
         mService.systemReady();
         mCm = new WrappedConnectivityManager(getContext(), mService);
         mCm.bindProcessToNetwork(null);
-
-        // Ensure that the default setting for Captive Portals is used for most tests
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
     }
 
     public void tearDown() throws Exception {
@@ -841,7 +819,7 @@
         return cv;
     }
 
-    @SmallTest
+    @LargeTest
     public void testLingering() throws Exception {
         verifyNoNetwork();
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -881,7 +859,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @LargeTest
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -916,7 +894,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @LargeTest
     public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
         // Test bringing up unvalidated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -942,7 +920,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @LargeTest
     public void testUnlingeringDoesNotValidate() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -970,7 +948,7 @@
                 NET_CAPABILITY_VALIDATED));
     }
 
-    @SmallTest
+    @LargeTest
     public void testCellularOutscoresWeakWifi() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -996,7 +974,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @LargeTest
     public void testReapingNetwork() throws Exception {
         // Test bringing up WiFi without NET_CAPABILITY_INTERNET.
         // Expect it to be torn down immediately because it satisfies no requests.
@@ -1029,7 +1007,7 @@
         waitFor(cv);
     }
 
-    @SmallTest
+    @LargeTest
     public void testCellularFallback() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1067,7 +1045,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @LargeTest
     public void testWiFiFallback() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -1099,8 +1077,7 @@
         NETWORK_CAPABILITIES,
         LINK_PROPERTIES,
         LOSING,
-        LOST,
-        UNAVAILABLE
+        LOST
     }
 
     /**
@@ -1141,11 +1118,6 @@
         }
 
         @Override
-        public void onUnavailable() {
-            setLastCallback(CallbackState.UNAVAILABLE, null, null);
-        }
-
-        @Override
         public void onLosing(Network network, int maxMsToLive) {
             setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
         }
@@ -1194,7 +1166,7 @@
         }
     }
 
-    @SmallTest
+    @LargeTest
     public void testStateChangeNetworkCallbacks() throws Exception {
         final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
         final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
@@ -1577,7 +1549,7 @@
         handlerThread.quit();
     }
 
-    @SmallTest
+    @LargeTest
     public void testNetworkFactoryRequests() throws Exception {
         tryNetworkFactoryRequests(NET_CAPABILITY_MMS);
         tryNetworkFactoryRequests(NET_CAPABILITY_SUPL);
@@ -1597,7 +1569,7 @@
         // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed.
     }
 
-    @SmallTest
+    @LargeTest
     public void testNoMutableNetworkRequests() throws Exception {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
         NetworkRequest.Builder builder = new NetworkRequest.Builder();
@@ -1622,7 +1594,7 @@
         } catch (IllegalArgumentException expected) {}
     }
 
-    @SmallTest
+    @LargeTest
     public void testMMSonWiFi() throws Exception {
         // Test bringing up cellular without MMS NetworkRequest gets reaped
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1657,7 +1629,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @LargeTest
     public void testMMSonCell() throws Exception {
         // Test bringing up cellular without MMS
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1683,7 +1655,7 @@
         verifyActiveNetwork(TRANSPORT_CELLULAR);
     }
 
-    @SmallTest
+    @LargeTest
     public void testCaptivePortal() {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
@@ -1733,47 +1705,6 @@
     }
 
     @SmallTest
-    public void testAvoidOrIgnoreCaptivePortals() {
-        final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
-        final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
-        mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
-
-        final TestNetworkCallback validatedCallback = new TestNetworkCallback();
-        final NetworkRequest validatedRequest = new NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_VALIDATED).build();
-        mCm.registerNetworkCallback(validatedRequest, validatedCallback);
-
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
-        // Bring up a network with a captive portal.
-        // Expect it to fail to connect and not result in any callbacks.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        String firstRedirectUrl = "http://example.com/firstPath";
-
-        ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV();
-        ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived();
-        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
-        waitFor(disconnectCv);
-        waitFor(avoidCv);
-
-        assertNoCallbacks(captivePortalCallback, validatedCallback);
-
-        // Now test ignore mode.
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
-
-        // Bring up a network with a captive portal.
-        // Since we're ignoring captive portals, the network will validate.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        String secondRedirectUrl = "http://example.com/secondPath";
-        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
-
-        // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        // But there should be no CaptivePortal callback.
-        captivePortalCallback.assertNoCallback();
-    }
-
-    @SmallTest
     public void testInvalidNetworkSpecifier() {
         boolean execptionCalled = true;
 
@@ -1803,7 +1734,7 @@
                 execptionCalled);
     }
 
-    @SmallTest
+    @LargeTest
     public void testRegisterDefaultNetworkCallback() throws Exception {
         final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
@@ -1864,7 +1795,7 @@
         }
     }
 
-    @SmallTest
+    @LargeTest
     public void testRequestCallbackUpdates() throws Exception {
         // File a network request for mobile.
         final TestNetworkCallback cellNetworkCallback = new TestRequestUpdateCallback();
@@ -1913,11 +1844,6 @@
         mCm.unregisterNetworkCallback(cellNetworkCallback);
     }
 
-    private void setCaptivePortalMode(int mode) {
-        ContentResolver cr = mServiceContext.getContentResolver();
-        Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
-    }
-
     private void setMobileDataAlwaysOn(boolean enable) {
         ContentResolver cr = mServiceContext.getContentResolver();
         Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
@@ -1999,7 +1925,6 @@
 
     @SmallTest
     public void testRequestBenchmark() throws Exception {
-        // TODO: turn this unit test into a real benchmarking test.
         // Benchmarks connecting and switching performance in the presence of a large number of
         // NetworkRequests.
         // 1. File NUM_REQUESTS requests.
@@ -2013,80 +1938,61 @@
         final CountDownLatch availableLatch = new CountDownLatch(NUM_REQUESTS);
         final CountDownLatch losingLatch = new CountDownLatch(NUM_REQUESTS);
 
+        final int REGISTER_TIME_LIMIT_MS = 100;
+        long startTime = System.currentTimeMillis();
         for (int i = 0; i < NUM_REQUESTS; i++) {
             callbacks[i] = new NetworkCallback() {
                 @Override public void onAvailable(Network n) { availableLatch.countDown(); }
                 @Override public void onLosing(Network n, int t) { losingLatch.countDown(); }
             };
+            mCm.registerNetworkCallback(request, callbacks[i]);
         }
+        long timeTaken = System.currentTimeMillis() - startTime;
+        String msg = String.format("Register %d callbacks: %dms, acceptable %dms",
+                NUM_REQUESTS, timeTaken, REGISTER_TIME_LIMIT_MS);
+        Log.d(TAG, msg);
+        assertTrue(msg, timeTaken < REGISTER_TIME_LIMIT_MS);
 
-        final int REGISTER_TIME_LIMIT_MS = 180;
-        assertTimeLimit("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> {
-            for (NetworkCallback cb : callbacks) {
-                mCm.registerNetworkCallback(request, cb);
-            }
-        });
-
-        final int CONNECT_TIME_LIMIT_MS = 40;
+        final int CONNECT_TIME_LIMIT_MS = 30;
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         // Don't request that the network validate, because otherwise connect() will block until
         // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired,
         // and we won't actually measure anything.
         mCellNetworkAgent.connect(false);
-
-        long onAvailableDispatchingDuration = durationOf(() -> {
-            if (!awaitLatch(availableLatch, CONNECT_TIME_LIMIT_MS)) {
-                fail(String.format("Only dispatched %d/%d onAvailable callbacks in %dms",
-                        NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS,
-                        CONNECT_TIME_LIMIT_MS));
-            }
-        });
+        startTime = System.currentTimeMillis();
+        if (!availableLatch.await(CONNECT_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) {
+            fail(String.format("Only dispatched %d/%d onAvailable callbacks in %dms",
+                    NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS,
+                    CONNECT_TIME_LIMIT_MS));
+        }
+        timeTaken = System.currentTimeMillis() - startTime;
         Log.d(TAG, String.format("Connect, %d callbacks: %dms, acceptable %dms",
-                NUM_REQUESTS, onAvailableDispatchingDuration, CONNECT_TIME_LIMIT_MS));
+                NUM_REQUESTS, timeTaken, CONNECT_TIME_LIMIT_MS));
 
-        final int SWITCH_TIME_LIMIT_MS = 40;
+        final int SWITCH_TIME_LIMIT_MS = 30;
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         // Give wifi a high enough score that we'll linger cell when wifi comes up.
         mWiFiNetworkAgent.adjustScore(40);
         mWiFiNetworkAgent.connect(false);
-
-        long onLostDispatchingDuration = durationOf(() -> {
-            if (!awaitLatch(losingLatch, SWITCH_TIME_LIMIT_MS)) {
-                fail(String.format("Only dispatched %d/%d onLosing callbacks in %dms",
-                        NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, SWITCH_TIME_LIMIT_MS));
-            }
-        });
+        startTime = System.currentTimeMillis();
+        if (!losingLatch.await(SWITCH_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) {
+            fail(String.format("Only dispatched %d/%d onLosing callbacks in %dms",
+                    NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, SWITCH_TIME_LIMIT_MS));
+        }
+        timeTaken = System.currentTimeMillis() - startTime;
         Log.d(TAG, String.format("Linger, %d callbacks: %dms, acceptable %dms",
-                NUM_REQUESTS, onLostDispatchingDuration, SWITCH_TIME_LIMIT_MS));
+                NUM_REQUESTS, timeTaken, SWITCH_TIME_LIMIT_MS));
 
         final int UNREGISTER_TIME_LIMIT_MS = 10;
-        assertTimeLimit("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> {
-            for (NetworkCallback cb : callbacks) {
-                mCm.unregisterNetworkCallback(cb);
-            }
-        });
-    }
-
-    private long durationOf(Runnable fn) {
-        long startTime = SystemClock.elapsedRealtime();
-        fn.run();
-        return SystemClock.elapsedRealtime() - startTime;
-    }
-
-    private void assertTimeLimit(String descr, long timeLimit, Runnable fn) {
-        long timeTaken = durationOf(fn);
-        String msg = String.format("%s: took %dms, limit was %dms", descr, timeTaken, timeLimit);
+        startTime = System.currentTimeMillis();
+        for (int i = 0; i < NUM_REQUESTS; i++) {
+            mCm.unregisterNetworkCallback(callbacks[i]);
+        }
+        timeTaken = System.currentTimeMillis() - startTime;
+        msg = String.format("Unregister %d callbacks: %dms, acceptable %dms",
+                NUM_REQUESTS, timeTaken, UNREGISTER_TIME_LIMIT_MS);
         Log.d(TAG, msg);
-        assertTrue(msg, timeTaken <= timeLimit);
-    }
-
-    private boolean awaitLatch(CountDownLatch l, long timeoutMs) {
-        try {
-            if (l.await(timeoutMs, TimeUnit.MILLISECONDS)) {
-                return true;
-            }
-        } catch (InterruptedException e) {}
-        return false;
+        assertTrue(msg, timeTaken < UNREGISTER_TIME_LIMIT_MS);
     }
 
     @SmallTest
@@ -2167,7 +2073,7 @@
             tracker.reevaluate();
             mService.waitForIdle();
             String msg = String.format("config=false, setting=%s", values[i]);
-            assertEventuallyTrue(() -> mService.avoidBadWifi(), 50);
+            assertTrue(msg, mService.avoidBadWifi());
             assertFalse(msg, tracker.shouldNotifyWifiUnvalidated());
         }
 
@@ -2176,19 +2082,19 @@
         Settings.Global.putInt(cr, settingName, 0);
         tracker.reevaluate();
         mService.waitForIdle();
-        assertEventuallyTrue(() -> !mService.avoidBadWifi(), 50);
+        assertFalse(mService.avoidBadWifi());
         assertFalse(tracker.shouldNotifyWifiUnvalidated());
 
         Settings.Global.putInt(cr, settingName, 1);
         tracker.reevaluate();
         mService.waitForIdle();
-        assertEventuallyTrue(() -> mService.avoidBadWifi(), 50);
+        assertTrue(mService.avoidBadWifi());
         assertFalse(tracker.shouldNotifyWifiUnvalidated());
 
         Settings.Global.putString(cr, settingName, null);
         tracker.reevaluate();
         mService.waitForIdle();
-        assertEventuallyTrue(() -> !mService.avoidBadWifi(), 50);
+        assertFalse(mService.avoidBadWifi());
         assertTrue(tracker.shouldNotifyWifiUnvalidated());
     }
 
@@ -2315,107 +2221,6 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
-    /**
-     * Validate that a satisfied network request does not trigger onUnavailable() once the
-     * time-out period expires.
-     */
-    @SmallTest
-    public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() {
-        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        mCm.requestNetwork(nr, networkCallback, 10);
-
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(false);
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-
-        // pass timeout and validate that UNAVAILABLE is not called
-        sleepFor(15);
-        networkCallback.assertNoCallback();
-    }
-
-    /**
-     * Validate that a satisfied network request followed by a disconnected (lost) network does
-     * not trigger onUnavailable() once the time-out period expires.
-     */
-    @SmallTest
-    public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() {
-        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        mCm.requestNetwork(nr, networkCallback, 500);
-
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(false);
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        sleepFor(20);
-        mWiFiNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-
-        // pass timeout and validate that UNAVAILABLE is not called
-        sleepFor(600);
-        networkCallback.assertNoCallback();
-    }
-
-    /**
-     * Validate that when a time-out is specified for a network request the onUnavailable()
-     * callback is called when time-out expires. Then validate that if network request is
-     * (somehow) satisfied - the callback isn't called later.
-     */
-    @SmallTest
-    public void testTimedoutNetworkRequest() {
-        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        mCm.requestNetwork(nr, networkCallback, 10);
-
-        // pass timeout and validate that UNAVAILABLE is called
-        networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
-
-        // create a network satisfying request - validate that request not triggered
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(false);
-        networkCallback.assertNoCallback();
-    }
-
-    /**
-     * Validate that when a network request is unregistered (cancelled) the time-out for that
-     * request doesn't trigger the onUnavailable() callback.
-     */
-    @SmallTest
-    public void testTimedoutAfterUnregisteredNetworkRequest() {
-        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        mCm.requestNetwork(nr, networkCallback, 10);
-
-        // remove request
-        mCm.unregisterNetworkCallback(networkCallback);
-
-        // pass timeout and validate that no callbacks
-        // Note: doesn't validate that nothing called from CS since even if called the CM already
-        // unregisters the callback and won't pass it through!
-        sleepFor(15);
-        networkCallback.assertNoCallback();
-
-        // create a network satisfying request - validate that request not triggered
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(false);
-        networkCallback.assertNoCallback();
-    }
-
-    public void assertEventuallyTrue(BooleanSupplier fn, long maxWaitingTimeMs) throws Exception {
-        long start = SystemClock.elapsedRealtime();
-        while (SystemClock.elapsedRealtime() <= start + maxWaitingTimeMs) {
-            if (fn.getAsBoolean()) {
-                return;
-            }
-            Thread.sleep(10);
-        }
-        assertTrue(fn.getAsBoolean());
-    }
-
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
@@ -2505,7 +2310,6 @@
         return mWiFiNetworkAgent.getNetwork();
     }
 
-    @SmallTest
     public void testPacketKeepalives() throws Exception {
         InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
         InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
@@ -2821,13 +2625,4 @@
             mCm.unregisterNetworkCallback(pendingIntent);
         }
     }
-
-    /* test utilities */
-    static private void sleepFor(int ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-        }
-
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index f841bf9..0d5daa5 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -24,7 +24,6 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import com.android.server.net.BaseNetworkObserver;
-import com.android.internal.util.test.BroadcastInterceptingContext;
 
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index a545af9..1a943a3 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -16,12 +16,17 @@
 
 package com.android.server;
 
+import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
 import static android.net.TrafficStats.KB_IN_BYTES;
@@ -29,42 +34,28 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.Time.TIMEZONE_UTC;
-
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
-import android.app.IUidObserver;
+import android.app.IProcessObserver;
 import android.app.Notification;
-import android.app.usage.UsageStatsManagerInternal;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
+import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -78,48 +69,39 @@
 import android.net.NetworkTemplate;
 import android.os.Binder;
 import android.os.INetworkManagementService;
-import android.os.PowerManagerInternal;
+import android.os.MessageQueue.IdleHandler;
 import android.os.UserHandle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import android.test.AndroidTestCase;
+import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.text.format.Time;
-import android.util.Log;
 import android.util.TrustedTime;
 
-import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.net.NetworkPolicyManagerService;
-
-import libcore.io.IoUtils;
-
 import com.google.common.util.concurrent.AbstractFuture;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 
 import java.io.File;
-import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
+import java.util.TimeZone;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.logging.Handler;
+
+import libcore.io.IoUtils;
 
 /**
  * Tests for {@link NetworkPolicyManagerService}.
  */
-@RunWith(AndroidJUnit4.class)
-public class NetworkPolicyManagerServiceTest {
+@LargeTest
+public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
     private static final String TAG = "NetworkPolicyManagerServiceTest";
 
     private static final long TEST_START = 1194220800000L;
@@ -131,19 +113,19 @@
     private BroadcastInterceptingContext mServiceContext;
     private File mPolicyDir;
 
-    private @Mock IActivityManager mActivityManager;
-    private @Mock INetworkStatsService mStatsService;
-    private @Mock INetworkManagementService mNetworkManager;
-    private @Mock TrustedTime mTime;
-    private @Mock IConnectivityManager mConnManager;
-    private @Mock INotificationManager mNotifManager;
-    private @Mock PackageManager mPackageManager;
+    private IActivityManager mActivityManager;
+    private INetworkStatsService mStatsService;
+    private INetworkManagementService mNetworkManager;
+    private INetworkPolicyListener mPolicyListener;
+    private TrustedTime mTime;
+    private IConnectivityManager mConnManager;
+    private INotificationManager mNotifManager;
 
-    private IUidObserver mUidObserver;
+    private NetworkPolicyManagerService mService;
+    private IProcessObserver mProcessObserver;
     private INetworkManagementEventObserver mNetworkObserver;
 
-    private NetworkPolicyListenerAnswer mPolicyListener;
-    private NetworkPolicyManagerService mService;
+    private Binder mStubBinder = new Binder();
 
     private long mStartTime;
     private long mElapsedRealtime;
@@ -156,30 +138,39 @@
     private static final int UID_A = UserHandle.getUid(USER_ID, APP_ID_A);
     private static final int UID_B = UserHandle.getUid(USER_ID, APP_ID_B);
 
-    private static final String PKG_NAME_A = "name.is.A,pkg.A";
+    private static final int PID_1 = 400;
+    private static final int PID_2 = 401;
+    private static final int PID_3 = 402;
 
-    @BeforeClass
-    public static void registerLocalServices() {
-        addLocalServiceMock(PowerManagerInternal.class);
-        addLocalServiceMock(DeviceIdleController.LocalService.class);
-        final UsageStatsManagerInternal usageStats =
-                addLocalServiceMock(UsageStatsManagerInternal.class);
-        when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
-    }
-
-    @Before
-    public void callSystemReady() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        final Context context = InstrumentationRegistry.getContext();
+    public void _setUp() throws Exception {
+        super.setUp();
 
         setCurrentTimeMillis(TEST_START);
 
         // intercept various broadcasts, and pretend that uids have packages
-        mServiceContext = new BroadcastInterceptingContext(context) {
+        mServiceContext = new BroadcastInterceptingContext(getContext()) {
             @Override
             public PackageManager getPackageManager() {
-                return mPackageManager;
+                return new MockPackageManager() {
+                    @Override
+                    public String[] getPackagesForUid(int uid) {
+                        return new String[] { "com.example" };
+                    }
+
+                    @Override
+                    public PackageInfo getPackageInfo(String packageName, int flags) {
+                        final PackageInfo info = new PackageInfo();
+                        final Signature signature;
+                        if ("android".equals(packageName)) {
+                            signature = new Signature("F00D");
+                        } else {
+                            signature = new Signature("DEAD");
+                        }
+                        info.signatures = new Signature[] { signature };
+                        return info;
+                    }
+
+                };
             }
 
             @Override
@@ -188,112 +179,229 @@
             }
         };
 
-        mPolicyDir = context.getFilesDir();
+        mPolicyDir = getContext().getFilesDir();
         if (mPolicyDir.exists()) {
             IoUtils.deleteContents(mPolicyDir);
         }
 
-        doAnswer(new Answer<Void>() {
+        mActivityManager = createMock(IActivityManager.class);
+        mStatsService = createMock(INetworkStatsService.class);
+        mNetworkManager = createMock(INetworkManagementService.class);
+        mPolicyListener = createMock(INetworkPolicyListener.class);
+        mTime = createMock(TrustedTime.class);
+        mConnManager = createMock(IConnectivityManager.class);
+        mNotifManager = createMock(INotificationManager.class);
 
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                mUidObserver = (IUidObserver) invocation.getArguments()[0];
-                Log.d(TAG, "set mUidObserver to " + mUidObserver);
-                return null;
-            }
-        }).when(mActivityManager).registerUidObserver(any(), anyInt());
-
-        mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mStatsService,
-                mNetworkManager, mTime, mPolicyDir, true);
+        mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
+                mStatsService, mNetworkManager, mTime, mPolicyDir, true);
         mService.bindConnectivityManager(mConnManager);
         mService.bindNotificationManager(mNotifManager);
-        mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
-        // Sets some common expectations.
-        when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenAnswer(
-                new Answer<PackageInfo>() {
+        // RemoteCallbackList needs a binder to use as key
+        expect(mPolicyListener.asBinder()).andReturn(mStubBinder).atLeastOnce();
+        replay();
+        mService.registerListener(mPolicyListener);
+        verifyAndReset();
 
-                    @Override
-                    public PackageInfo answer(InvocationOnMock invocation) throws Throwable {
-                        final String packageName = (String) invocation.getArguments()[0];
-                        final PackageInfo info = new PackageInfo();
-                        final Signature signature;
-                        if ("android".equals(packageName)) {
-                            signature = new Signature("F00D");
-                        } else {
-                            signature = new Signature("DEAD");
-                        }
-                        info.signatures = new Signature[] {
-                            signature
-                        };
-                        return info;
-                    }
-                });
-        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
-                .thenReturn(new ApplicationInfo());
-        when(mPackageManager.getPackagesForUid(UID_A)).thenReturn(new String[] {PKG_NAME_A});
-        when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
-        expectCurrentTime();
-
-        // Prepare NPMS.
-        mService.systemReady();
+        // catch IProcessObserver during systemReady()
+        final Capture<IProcessObserver> processObserver = new Capture<IProcessObserver>();
+        mActivityManager.registerProcessObserver(capture(processObserver));
+        expectLastCall().atLeastOnce();
 
         // catch INetworkManagementEventObserver during systemReady()
-        ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
-              ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
-        verify(mNetworkManager).registerObserver(networkObserver.capture());
+        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
+                INetworkManagementEventObserver>();
+        mNetworkManager.registerObserver(capture(networkObserver));
+        expectLastCall().atLeastOnce();
+
+        expect(mNetworkManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+        expectCurrentTime();
+
+        replay();
+        mService.systemReady();
+        verifyAndReset();
+
+        mProcessObserver = processObserver.getValue();
         mNetworkObserver = networkObserver.getValue();
+
     }
 
-    @After
-    public void removeFiles() throws Exception {
+    public void _tearDown() throws Exception {
         for (File file : mPolicyDir.listFiles()) {
             file.delete();
         }
+
+        mServiceContext = null;
+        mPolicyDir = null;
+
+        mActivityManager = null;
+        mStatsService = null;
+        mPolicyListener = null;
+        mTime = null;
+
+        mService = null;
+        mProcessObserver = null;
+
+        super.tearDown();
     }
 
-    @After
-    public void unregisterLocalServices() throws Exception {
-        // Registered by NetworkPolicyManagerService's constructor.
-        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
-    }
-
-    // NOTE: testPolicyChangeTriggersListener() and testUidForeground() are too superficial, they
-    // don't check for side-effects (like calls to NetworkManagementService) neither cover all
-    // different modes (Data Saver, Battery Saver, Doze, App idle, etc...).
-    // These scenarios are extensively tested on CTS' HostsideRestrictBackgroundNetworkTests.
-
-    @Test
-    public void testPolicyChangeTriggersListener() throws Exception {
-        mPolicyListener.expect().onRestrictBackgroundBlacklistChanged(anyInt(), anyBoolean());
-
+    @Suppress
+    public void testPolicyChangeTriggersBroadcast() throws Exception {
         mService.setUidPolicy(APP_ID_A, POLICY_NONE);
+
+        // change background policy and expect broadcast
+        final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
+                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+
         mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
 
-        mPolicyListener.waitAndVerify().onRestrictBackgroundBlacklistChanged(APP_ID_A, true);
+        backgroundChanged.get();
     }
 
-    @Test
-    public void testUidForeground() throws Exception {
-        // push all uids into background
-        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE);
-        mUidObserver.onUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE);
+    @Suppress
+    public void testPidForegroundCombined() throws Exception {
+        IdleFuture idle;
+
+        // push all uid into background
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
+        idle.get();
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
-        // push one of the uids into foreground
-        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP);
+        // push one of the shared pids into foreground
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        idle.get();
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
-        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE);
-        mUidObserver.onUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP);
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
+        idle.get();
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
+
+        // push both pid into foreground
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        idle.get();
+        assertTrue(mService.isUidForeground(UID_A));
+
+        // pull one out, should still be foreground
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        idle.get();
+        assertTrue(mService.isUidForeground(UID_A));
+
+        // pull final pid out, should now be background
+        idle = expectIdle();
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        idle.get();
+        assertFalse(mService.isUidForeground(UID_A));
     }
 
-    @Test
+    @Suppress
+    public void testPolicyNone() throws Exception {
+        Future<Void> future;
+
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
+        verifyAndReset();
+
+        // POLICY_NONE should RULE_ALLOW in foreground
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
+        future.get();
+        verifyAndReset();
+
+        // POLICY_NONE should RULE_ALLOW in background
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        future.get();
+        verifyAndReset();
+    }
+
+    @Suppress
+    public void testPolicyReject() throws Exception {
+        Future<Void> future;
+
+        // POLICY_REJECT should RULE_ALLOW in background
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        replay();
+        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
+        verifyAndReset();
+
+        // POLICY_REJECT should RULE_ALLOW in foreground
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
+        verifyAndReset();
+
+        // POLICY_REJECT should RULE_REJECT in background
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        future.get();
+        verifyAndReset();
+    }
+
+    @Suppress
+    public void testPolicyRejectAddRemove() throws Exception {
+        Future<Void> future;
+
+        // POLICY_NONE should have RULE_ALLOW in background
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
+        future.get();
+        verifyAndReset();
+
+        // adding POLICY_REJECT should cause RULE_REJECT
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        replay();
+        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
+        verifyAndReset();
+
+        // removing POLICY_REJECT should return us to RULE_ALLOW
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
+        future.get();
+        verifyAndReset();
+    }
+
     public void testLastCycleBoundaryThisMonth() throws Exception {
         // assume cycle day of "5th", which should be in same month
         final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
@@ -305,7 +413,6 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
     public void testLastCycleBoundaryLastMonth() throws Exception {
         // assume cycle day of "20th", which should be in last month
         final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
@@ -317,7 +424,6 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
     public void testLastCycleBoundaryThisMonthFebruary() throws Exception {
         // assume cycle day of "30th" in february; should go to january
         final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
@@ -329,7 +435,6 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
     public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
         // assume cycle day of "30th" in february, which should clamp
         final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
@@ -341,7 +446,6 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
     public void testCycleBoundaryLeapYear() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 29, TIMEZONE_UTC, 1024L, 1024L, false);
@@ -365,7 +469,6 @@
                 computeNextCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
     }
 
-    @Test
     public void testNextCycleTimezoneAfterUtc() throws Exception {
         // US/Central is UTC-6
         final NetworkPolicy policy = new NetworkPolicy(
@@ -374,7 +477,6 @@
                 computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy));
     }
 
-    @Test
     public void testNextCycleTimezoneBeforeUtc() throws Exception {
         // Israel is UTC+2
         final NetworkPolicy policy = new NetworkPolicy(
@@ -383,7 +485,6 @@
                 computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy));
     }
 
-    @Test
     public void testNextCycleSane() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false);
@@ -399,7 +500,6 @@
         }
     }
 
-    @Test
     public void testLastCycleSane() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false);
@@ -415,7 +515,6 @@
         }
     }
 
-    @Test
     public void testCycleTodayJanuary() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 14, "US/Pacific", 1024L, 1024L, false);
@@ -435,7 +534,6 @@
                 computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
     }
 
-    @Test
     public void testLastCycleBoundaryDST() throws Exception {
         final long currentTime = parseTime("1989-01-02T07:30:00.000");
         final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
@@ -446,7 +544,6 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
     public void testLastCycleBoundaryJanuaryDST() throws Exception {
         final long currentTime = parseTime("1989-01-26T21:00:00.000Z");
         final long expectedCycle = parseTime("1989-01-01T01:59:59.000Z");
@@ -457,10 +554,11 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Test
+    @Suppress
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
+        Future<Void> future;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -471,40 +569,75 @@
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
         state = new NetworkState[] { buildWifi() };
-        when(mConnManager.getAllNetworkState()).thenReturn(state);
+        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
         expectCurrentTime();
+        expectClearNotifications();
+        expectAdvisePersistThreshold();
+        future = expectMeteredIfacesChanged();
 
-        mPolicyListener.expect().onMeteredIfacesChanged(any());
+        replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
-        mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
+        future.get();
+        verifyAndReset();
 
         // now change cycle to be on 15th, and test in early march, to verify we
         // pick cycle day in previous month.
-        when(mConnManager.getAllNetworkState()).thenReturn(state);
+        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
         expectCurrentTime();
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
-                .thenReturn(stats.getTotalBytes());
-
-        mPolicyListener.expect().onMeteredIfacesChanged(any());
-        setNetworkPolicies(new NetworkPolicy(
-                sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
-        mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
+        expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
+                .andReturn(stats.getTotalBytes()).atLeastOnce();
+        expectPolicyDataEnable(TYPE_WIFI, true);
 
         // TODO: consider making strongly ordered mock
-        verifyPolicyDataEnable(TYPE_WIFI, true);
-        verifyRemoveInterfaceQuota(TEST_IFACE);
-        verifySetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
+        expectRemoveInterfaceQuota(TEST_IFACE);
+        expectSetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
+
+        expectClearNotifications();
+        expectAdvisePersistThreshold();
+        future = expectMeteredIfacesChanged(TEST_IFACE);
+
+        replay();
+        setNetworkPolicies(new NetworkPolicy(
+                sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
+        future.get();
+        verifyAndReset();
     }
 
-    @Test
+    @Suppress
+    public void testUidRemovedPolicyCleared() throws Exception {
+        Future<Void> future;
+
+        // POLICY_REJECT should RULE_REJECT in background
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        replay();
+        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
+        verifyAndReset();
+
+        // uninstall should clear RULE_REJECT
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
+        expectSetUidForeground(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        final Intent intent = new Intent(ACTION_UID_REMOVED);
+        intent.putExtra(EXTRA_UID, UID_A);
+        mServiceContext.sendBroadcast(intent);
+        future.get();
+        verifyAndReset();
+    }
+
+    @Suppress
     public void testOverWarningLimitNotification() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
-        Future<String> tagFuture = null;
+        Future<Void> future;
+        Future<String> tagFuture;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -519,15 +652,20 @@
 
         {
             expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true);
 
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
+            expectClearNotifications();
+            expectAdvisePersistThreshold();
+            future = expectMeteredIfacesChanged();
+
+            replay();
             setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
                     * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
-            verifyPolicyDataEnable(TYPE_WIFI, true);
+            future.get();
+            verifyAndReset();
         }
 
         // bring up wifi network
@@ -538,17 +676,22 @@
 
         {
             expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true);
 
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
+            expectRemoveInterfaceQuota(TEST_IFACE);
+            expectSetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
+
+            expectClearNotifications();
+            expectAdvisePersistThreshold();
+            future = expectMeteredIfacesChanged(TEST_IFACE);
+
+            replay();
             mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
+            future.get();
+            verifyAndReset();
         }
 
         // go over warning, which should kick notification
@@ -558,15 +701,18 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true);
+
+            expectForceUpdate();
+            expectClearNotifications();
             tagFuture = expectEnqueueNotification();
 
+            replay();
             mNetworkObserver.limitReached(null, TEST_IFACE);
-
             assertNotificationType(TYPE_WARNING, tagFuture.get());
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-
+            verifyAndReset();
         }
 
         // go over limit, which should kick notification and dialog
@@ -576,14 +722,18 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, false);
+
+            expectForceUpdate();
+            expectClearNotifications();
             tagFuture = expectEnqueueNotification();
 
+            replay();
             mNetworkObserver.limitReached(null, TEST_IFACE);
-
             assertNotificationType(TYPE_LIMIT, tagFuture.get());
-            verifyPolicyDataEnable(TYPE_WIFI, false);
+            verifyAndReset();
         }
 
         // now snooze policy, which should remove quota
@@ -591,28 +741,35 @@
 
         {
             expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
-            tagFuture = expectEnqueueNotification();
+            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true);
 
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
-            mService.snoozeLimit(sTemplateWifi);
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
-            assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
             // snoozed interface still has high quota so background data is
             // still restricted.
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
-            verifyPolicyDataEnable(TYPE_WIFI, true);
+            expectRemoveInterfaceQuota(TEST_IFACE);
+            expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+            expectAdvisePersistThreshold();
+            expectMeteredIfacesChanged(TEST_IFACE);
+
+            future = expectClearNotifications();
+            tagFuture = expectEnqueueNotification();
+
+            replay();
+            mService.snoozeLimit(sTemplateWifi);
+            assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
+            future.get();
+            verifyAndReset();
         }
     }
 
-    @Test
+    @Suppress
     public void testMeteredNetworkWithoutLimit() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
+        Future<Void> future;
+        Future<String> tagFuture;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -627,19 +784,24 @@
 
         {
             expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats.getTotalBytes()).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true);
 
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
+            expectRemoveInterfaceQuota(TEST_IFACE);
+            expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+
+            expectClearNotifications();
+            expectAdvisePersistThreshold();
+            future = expectMeteredIfacesChanged(TEST_IFACE);
+
+            replay();
             setNetworkPolicies(new NetworkPolicy(
                     sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED,
                     true));
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+            future.get();
+            verifyAndReset();
         }
     }
 
@@ -662,36 +824,87 @@
     }
 
     private void expectCurrentTime() throws Exception {
-        when(mTime.forceRefresh()).thenReturn(false);
-        when(mTime.hasCache()).thenReturn(true);
-        when(mTime.currentTimeMillis()).thenReturn(currentTimeMillis());
-        when(mTime.getCacheAge()).thenReturn(0L);
-        when(mTime.getCacheCertainty()).thenReturn(0L);
+        expect(mTime.forceRefresh()).andReturn(false).anyTimes();
+        expect(mTime.hasCache()).andReturn(true).anyTimes();
+        expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
+        expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
+        expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
+    }
+
+    private void expectForceUpdate() throws Exception {
+        mStatsService.forceUpdate();
+        expectLastCall().atLeastOnce();
+    }
+
+    private Future<Void> expectClearNotifications() throws Exception {
+        final FutureAnswer future = new FutureAnswer();
+        mNotifManager.cancelNotificationWithTag(
+                isA(String.class), isA(String.class), anyInt(), anyInt());
+        expectLastCall().andAnswer(future).anyTimes();
+        return future;
     }
 
     private Future<String> expectEnqueueNotification() throws Exception {
-        final FutureAnswer<String> futureAnswer = new FutureAnswer<String>(2);
-        doAnswer(futureAnswer).when(mNotifManager).enqueueNotificationWithTag(
-                anyString(), anyString(), anyString() /* capture here (index 2)*/,
-                anyInt(), isA(Notification.class), isA(int[].class), anyInt());
-        return futureAnswer;
+        final FutureCapture<String> tag = new FutureCapture<String>();
+        mNotifManager.enqueueNotificationWithTag(isA(String.class), isA(String.class),
+                capture(tag.capture), anyInt(),
+                isA(Notification.class), isA(int[].class), UserHandle.myUserId());
+        return tag;
     }
 
-    private void verifySetInterfaceQuota(String iface, long quotaBytes) throws Exception {
-        verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(iface, quotaBytes);
+    private void expectSetInterfaceQuota(String iface, long quotaBytes) throws Exception {
+        mNetworkManager.setInterfaceQuota(iface, quotaBytes);
+        expectLastCall().atLeastOnce();
     }
 
-    private void verifyRemoveInterfaceQuota(String iface) throws Exception {
-        verify(mNetworkManager, atLeastOnce()).removeInterfaceQuota(iface);
+    private void expectRemoveInterfaceQuota(String iface) throws Exception {
+        mNetworkManager.removeInterfaceQuota(iface);
+        expectLastCall().atLeastOnce();
     }
 
-    private Future<Void> verifyPolicyDataEnable(int type, boolean enabled) throws Exception {
+    private void expectSetInterfaceAlert(String iface, long alertBytes) throws Exception {
+        mNetworkManager.setInterfaceAlert(iface, alertBytes);
+        expectLastCall().atLeastOnce();
+    }
+
+    private void expectRemoveInterfaceAlert(String iface) throws Exception {
+        mNetworkManager.removeInterfaceAlert(iface);
+        expectLastCall().atLeastOnce();
+    }
+
+    private void expectSetUidMeteredNetworkBlacklist(int uid, boolean rejectOnQuotaInterfaces)
+            throws Exception {
+        mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
+        expectLastCall().atLeastOnce();
+    }
+
+    private void expectSetUidForeground(int uid, boolean uidForeground) throws Exception {
+        mStatsService.setUidForeground(uid, uidForeground);
+        expectLastCall().atLeastOnce();
+    }
+
+    private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
+        final FutureAnswer future = new FutureAnswer();
+        mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+        expectLastCall().andAnswer(future);
+        return future;
+    }
+
+    private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception {
+        final FutureAnswer future = new FutureAnswer();
+        mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
+        expectLastCall().andAnswer(future);
+        return future;
+    }
+
+    private Future<Void> expectPolicyDataEnable(int type, boolean enabled) throws Exception {
         // TODO: bring back this test
         return null;
     }
 
-    private void verifyAdvisePersistThreshold() throws Exception {
-        verify(mStatsService).advisePersistThreshold(anyLong());
+    private void expectAdvisePersistThreshold() throws Exception {
+        mStatsService.advisePersistThreshold(anyLong());
+        expectLastCall().anyTimes();
     }
 
     private static class TestAbstractFuture<T> extends AbstractFuture<T> {
@@ -705,21 +918,50 @@
         }
     }
 
-    private static class FutureAnswer<T> extends TestAbstractFuture<T> implements Answer<Void> {
-        private final int index;
-
-        FutureAnswer(int index) {
-            this.index = index;
-        }
+    private static class FutureAnswer extends TestAbstractFuture<Void> implements IAnswer<Void> {
         @Override
-        public Void answer(InvocationOnMock invocation) throws Throwable {
-            @SuppressWarnings("unchecked")
-            T captured = (T) invocation.getArguments()[index];
-            set(captured);
+        public Void answer() {
+            set(null);
             return null;
         }
     }
 
+    private static class FutureCapture<T> extends TestAbstractFuture<T> {
+        public Capture<T> capture = new Capture<T>() {
+            @Override
+            public void setValue(T value) {
+                super.setValue(value);
+                set(value);
+            }
+        };
+    }
+
+    private static class IdleFuture extends AbstractFuture<Void> implements IdleHandler {
+        @Override
+        public Void get() throws InterruptedException, ExecutionException {
+            try {
+                return get(5, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public boolean queueIdle() {
+            set(null);
+            return false;
+        }
+    }
+
+    /**
+     * Wait until {@link #mService} internal {@link Handler} is idle.
+     */
+    private IdleFuture expectIdle() {
+        final IdleFuture future = new IdleFuture();
+        mService.addIdleHandler(future);
+        return future;
+    }
+
     private static void assertTimeEquals(long expected, long actual) {
         if (expected != actual) {
             fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
@@ -747,7 +989,7 @@
     }
 
     private static void assertNotificationType(int expected, String actualTag) {
-        assertEquals("notification type mismatch for '" + actualTag +"'",
+        assertEquals(
                 Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
     }
 
@@ -768,59 +1010,15 @@
         mElapsedRealtime += duration;
     }
 
-    /**
-     * Creates a mock and registers it to {@link LocalServices}.
-     */
-    private static <T> T addLocalServiceMock(Class<T> clazz) {
-        final T mock = mock(clazz);
-        LocalServices.addService(clazz, mock);
-        return mock;
+    private void replay() {
+        EasyMock.replay(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+                mConnManager, mNotifManager);
     }
 
-    /**
-     * Custom Mockito answer used to verify async {@link INetworkPolicyListener} calls.
-     *
-     * <p>Typical usage:
-     * <pre><code>
-     *    mPolicyListener.expect().someCallback(any());
-     *    // do something on objects under test
-     *    mPolicyListener.waitAndVerify().someCallback(eq(expectedValue));
-     * </code></pre>
-     */
-    final class NetworkPolicyListenerAnswer implements Answer<Void> {
-        private CountDownLatch latch;
-        private final INetworkPolicyListener listener;
-
-        NetworkPolicyListenerAnswer(NetworkPolicyManagerService service) {
-            this.listener = mock(INetworkPolicyListener.class);
-            // RemoteCallbackList needs a binder to use as key
-            when(listener.asBinder()).thenReturn(new Binder());
-            service.registerListener(listener);
-        }
-
-        @Override
-        public Void answer(InvocationOnMock invocation) throws Throwable {
-            Log.d(TAG,"counting down on answer: " + invocation);
-            latch.countDown();
-            return null;
-        }
-
-        INetworkPolicyListener expect() {
-            assertNull("expect() called before waitAndVerify()", latch);
-            latch = new CountDownLatch(1);
-            return doAnswer(this).when(listener);
-        }
-
-        INetworkPolicyListener waitAndVerify() {
-            assertNotNull("waitAndVerify() called before expect()", latch);
-            try {
-                assertTrue("callback not called in 5 seconds", latch.await(5, TimeUnit.SECONDS));
-            } catch (InterruptedException e) {
-                fail("Thread interrupted before callback called");
-            } finally {
-                latch = null;
-            }
-            return verify(listener, atLeastOnce());
-        }
+    private void verifyAndReset() {
+        EasyMock.verify(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+                mConnManager, mNotifManager);
+        EasyMock.reset(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
+                mConnManager, mNotifManager);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 69d27f2..af570b0 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -53,6 +53,7 @@
 import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.net.RecommendationRequest;
@@ -134,9 +135,10 @@
 
         mNetworkScoreService.systemRunning();
 
-        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
-                new ComponentName(NEW_SCORER.packageName,
-                    NEW_SCORER.recommendationServiceClassName))),
+        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
+                new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+                        .setComponent(new ComponentName(NEW_SCORER.packageName,
+                                NEW_SCORER.recommendationServiceClassName))),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.SYSTEM));
@@ -145,11 +147,11 @@
     @Test
     public void testRequestScores_noPermission() throws Exception {
         doThrow(new SecurityException()).when(mContext)
-            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+            .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES),
                 anyString());
         try {
             mNetworkScoreService.requestScores(null);
-            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+            fail("REQUEST_NETWORK_SCORES not enforced.");
         } catch (SecurityException e) {
             // expected
         }
@@ -182,11 +184,11 @@
     @Test
     public void testRequestRecommendation_noPermission() throws Exception {
         doThrow(new SecurityException()).when(mContext)
-            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+            .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES),
                 anyString());
         try {
             mNetworkScoreService.requestRecommendation(mRecommendationRequest);
-            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+            fail("REQUEST_NETWORK_SCORES not enforced.");
         } catch (SecurityException e) {
             // expected
         }
@@ -320,9 +322,9 @@
     }
 
     @Test
-    public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
+    public void testClearScores_notActiveScorer_noRequestNetworkScoresPermission() {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
-        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+        when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
             .thenReturn(PackageManager.PERMISSION_DENIED);
         try {
             mNetworkScoreService.clearScores();
@@ -333,9 +335,9 @@
     }
 
     @Test
-    public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
+    public void testClearScores_activeScorer_noRequestNetworkScoresPermission() {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
-        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+        when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
             .thenReturn(PackageManager.PERMISSION_DENIED);
 
         mNetworkScoreService.clearScores();
@@ -353,10 +355,10 @@
     }
 
     @Test
-    public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
+    public void testClearScores_notActiveScorer_hasRequestNetworkScoresPermission()
             throws RemoteException {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
-        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+        when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
 
         mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
@@ -380,9 +382,9 @@
     }
 
     @Test
-    public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
+    public void testDisableScoring_notActiveScorer_noRequestNetworkScoresPermission() {
         when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
-        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+        when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
 
         try {
@@ -394,9 +396,9 @@
     }
 
     @Test
-    public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
+    public void testRegisterNetworkScoreCache_noRequestNetworkScoresPermission() {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
-                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
+                eq(permission.REQUEST_NETWORK_SCORES), anyString());
 
         try {
             mNetworkScoreService.registerNetworkScoreCache(
@@ -408,9 +410,9 @@
     }
 
     @Test
-    public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
+    public void testUnregisterNetworkScoreCache_noRequestNetworkScoresPermission() {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
-                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
+                eq(permission.REQUEST_NETWORK_SCORES), anyString());
 
         try {
             mNetworkScoreService.unregisterNetworkScoreCache(
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
similarity index 95%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 84f0f90..aed3635 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -71,8 +71,7 @@
                 "    transport_types: 3",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -94,8 +93,7 @@
                 "    state_transition: \"SomeState\"",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -116,8 +114,7 @@
                 "    state_transition: \"\"",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -163,8 +160,7 @@
                 "    return_codes: 178",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -185,8 +181,7 @@
                 "    latency_ms: 5678",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -205,8 +200,7 @@
                 "    if_name: \"wlan0\"",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -229,8 +223,7 @@
                 "    >",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -255,8 +248,7 @@
                 "    probe_result: 204",
                 "    probe_type: 1",
                 "  >",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -282,8 +274,7 @@
                 "    program_length: 2048",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -314,8 +305,7 @@
                 "    zero_lifetime_ras: 1",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
@@ -342,8 +332,7 @@
                 "    router_lifetime: 2000",
                 "  >",
                 "  time_ms: 1",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, ev);
     }
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
similarity index 91%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
index aa491bb..3fc89b9 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
-import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.ApfStats;
 import android.net.metrics.DefaultNetworkEvent;
 import android.net.metrics.DhcpClientEvent;
@@ -58,7 +57,7 @@
 
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
+        mService = new IpConnectivityMetrics(mCtx);
     }
 
     public void testLoggingEvents() throws Exception {
@@ -113,27 +112,6 @@
         assertEquals("", output3);
     }
 
-    public void testRateLimiting() {
-        final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
-        final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
-        final long fakeTimestamp = 1;
-
-        int attempt = 100; // More than burst quota, but less than buffer size.
-        for (int i = 0; i < attempt; i++) {
-            logger.log(ev);
-        }
-
-        String output1 = getdump("flush");
-        assertFalse("".equals(output1));
-
-        for (int i = 0; i < attempt; i++) {
-            assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
-        }
-
-        String output2 = getdump("flush");
-        assertEquals("", output2);
-    }
-
     public void testEndToEndLogging() {
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
 
@@ -226,8 +204,7 @@
                 "    router_lifetime: 2000",
                 "  >",
                 "  time_ms: 700",
-                ">",
-                "version: 2");
+                ">");
 
         verifySerialization(want, getdump("flush"));
     }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/services/tests/servicestests/src/com/android/server/connectivity/LingerMonitorTest.java
similarity index 97%
rename from tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/LingerMonitorTest.java
index 77956be..bce5787e 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/LingerMonitorTest.java
@@ -24,7 +24,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.format.DateUtils;
 import com.android.internal.R;
 import com.android.server.ConnectivityService;
@@ -71,7 +70,6 @@
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
 
-    @SmallTest
     public void testTransitions() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         NetworkAgentInfo nai1 = wifiNai(100);
@@ -81,7 +79,6 @@
         assertFalse(mMonitor.isNotificationEnabled(nai2, nai1));
     }
 
-    @SmallTest
     public void testNotificationOnLinger() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -92,7 +89,6 @@
         verifyNotification(from, to);
     }
 
-    @SmallTest
     public void testToastOnLinger() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
@@ -103,7 +99,6 @@
         verifyToast(from, to);
     }
 
-    @SmallTest
     public void testNotificationClearedAfterDisconnect() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -117,7 +112,6 @@
         verify(mNotifier, times(1)).clearNotification(100);
     }
 
-    @SmallTest
     public void testNotificationClearedAfterSwitchingBack() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -131,7 +125,6 @@
         verify(mNotifier, times(1)).clearNotification(100);
     }
 
-    @SmallTest
     public void testUniqueToast() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
@@ -149,7 +142,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testMultipleNotifications() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -168,7 +160,6 @@
         verifyNotification(wifi2, cell);
     }
 
-    @SmallTest
     public void testRateLimiting() throws InterruptedException {
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, LOW_RATE_LIMIT);
 
@@ -194,7 +185,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testDailyLimiting() throws InterruptedException {
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, LOW_DAILY_LIMIT, HIGH_RATE_LIMIT);
 
@@ -221,7 +211,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testUniqueNotification() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -238,7 +227,6 @@
         verifyNotification(from, to);
     }
 
-    @SmallTest
     public void testIgnoreNeverValidatedNetworks() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -250,7 +238,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testIgnoreCurrentlyValidatedNetworks() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -262,7 +249,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testNoNotificationType() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch();
@@ -273,7 +259,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testNoTransitionToNotify() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NONE);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -284,7 +269,6 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
     public void testDifferentTransitionToNotify() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(CELLULAR, WIFI));
diff --git a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java
similarity index 97%
rename from tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java
index 5981f48..5f84ea1 100644
--- a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java
@@ -20,7 +20,6 @@
 import android.net.ConnectivityMetricsEvent;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
 import static android.net.ConnectivityMetricsEvent.Reference;
 
 import junit.framework.TestCase;
@@ -68,14 +67,12 @@
         mService.onStart();
     }
 
-    @SmallTest
     public void testGetNoEvents() throws Exception {
         Reference r = new Reference(0);
         assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
         assertEquals(0, r.getValue());
     }
 
-    @SmallTest
     public void testLogAndGetEvents() throws Exception {
         mService.mBinder.logEvents(EVENTS);
 
@@ -88,7 +85,6 @@
         assertEquals(N_EVENTS, r.getValue());
     }
 
-    @SmallTest
     public void testLogOneByOne() throws Exception {
         for (ConnectivityMetricsEvent ev : EVENTS) {
             mService.mBinder.logEvent(ev);
@@ -103,7 +99,6 @@
         assertEquals(N_EVENTS, r.getValue());
     }
 
-    @SmallTest
     public void testInterleavedLogAndGet() throws Exception {
         mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3));
 
@@ -122,7 +117,6 @@
         assertEquals(N_EVENTS, r.getValue());
     }
 
-    @SmallTest
     public void testMultipleGetAll() throws Exception {
         mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3));
 
@@ -137,7 +131,6 @@
         assertEquals(N_EVENTS, r2.getValue());
     }
 
-    @SmallTest
     public void testLogAndDumpConcurrently() throws Exception {
         for (int i = 0; i < 50; i++) {
             mContext = null;
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
rename to services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
similarity index 93%
rename from tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 2bb62bb..9e2fd62 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -22,8 +22,6 @@
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 import org.junit.Before;
@@ -84,7 +82,6 @@
         verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
     }
 
-    @SmallTest
     public void testOneBatch() throws Exception {
         log(105, LATENCIES);
         log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
@@ -99,7 +96,6 @@
             new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
-    @SmallTest
     public void testSeveralBatches() throws Exception {
         log(105, LATENCIES);
         log(106, LATENCIES);
@@ -113,7 +109,6 @@
             new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
-    @SmallTest
     public void testBatchAndNetworkLost() throws Exception {
         byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
         byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
@@ -130,7 +125,6 @@
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
-    @SmallTest
     public void testConcurrentBatchesAndDumps() throws Exception {
         final long stop = System.currentTimeMillis() + 100;
         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
@@ -152,7 +146,6 @@
             new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
-    @SmallTest
     public void testConcurrentBatchesAndNetworkLoss() throws Exception {
         logAsync(105, LATENCIES);
         Thread.sleep(10L);
@@ -164,13 +157,9 @@
     }
 
     void log(int netId, int[] latencies) {
-        try {
-            for (int l : latencies) {
-                mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
-                        0, 0);
-            }
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
+        for (int l : latencies) {
+            mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null, 0,
+                    0);
         }
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/TetheringTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java
similarity index 77%
rename from tests/net/java/com/android/server/connectivity/VpnTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java
index b51b277..5d8b843 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java
@@ -25,11 +25,9 @@
 
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.UidRange;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -45,8 +43,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -92,18 +88,14 @@
     @Mock private PackageManager mPackageManager;
     @Mock private INetworkManagementService mNetService;
     @Mock private AppOpsManager mAppOps;
-    @Mock private NotificationManager mNotificationManager;
 
     @Override
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         setMockedPackages(mPackages);
-        when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
-        when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
-                .thenReturn(mNotificationManager);
         doNothing().when(mNetService).registerObserver(any());
     }
 
@@ -111,7 +103,7 @@
     public void testRestrictedProfilesAreAddedToVpn() {
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final Vpn vpn = new MockVpn(primaryUser.id);
         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
                 null, null);
 
@@ -125,7 +117,7 @@
     public void testManagedProfilesAreNotAddedToVpn() {
         setMockedUsers(primaryUser, managedProfileA);
 
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final Vpn vpn = new MockVpn(primaryUser.id);
         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
                 null, null);
 
@@ -138,7 +130,7 @@
     public void testAddUserToVpnOnlyAddsOneUser() {
         setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
 
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final Vpn vpn = new MockVpn(primaryUser.id);
         final Set<UidRange> ranges = new ArraySet<>();
         vpn.addUserToRanges(ranges, primaryUser.id, null, null);
 
@@ -149,7 +141,7 @@
 
     @SmallTest
     public void testUidWhiteAndBlacklist() throws Exception {
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final Vpn vpn = new MockVpn(primaryUser.id);
         final UidRange user = UidRange.createForUser(primaryUser.id);
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
 
@@ -174,15 +166,15 @@
 
     @SmallTest
     public void testLockdownChangingPackage() throws Exception {
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final MockVpn vpn = new MockVpn(primaryUser.id);
         final UidRange user = UidRange.createForUser(primaryUser.id);
 
         // Default state.
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        vpn.assertUnblocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on without lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        vpn.assertUnblocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on with lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
@@ -190,8 +182,8 @@
             new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
             new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
-        assertUnblocked(vpn, user.start + PKG_UIDS[1]);
+        vpn.assertBlocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        vpn.assertUnblocked(user.start + PKG_UIDS[1]);
 
         // Switch to another app.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
@@ -203,13 +195,13 @@
             new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
             new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
-        assertUnblocked(vpn, user.start + PKG_UIDS[3]);
+        vpn.assertBlocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+        vpn.assertUnblocked(user.start + PKG_UIDS[3]);
     }
 
     @SmallTest
     public void testLockdownAddingAProfile() throws Exception {
-        final Vpn vpn = spyVpn(primaryUser.id);
+        final MockVpn vpn = new MockVpn(primaryUser.id);
         setMockedUsers(primaryUser);
 
         // Make a copy of the restricted profile, as we're going to mark it deleted halfway through.
@@ -228,7 +220,7 @@
         }));
 
         // Verify restricted user isn't affected at first.
-        assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
+        vpn.assertUnblocked(profile.start + PKG_UIDS[0]);
 
         // Add the restricted user.
         setMockedUsers(primaryUser, tempProfile);
@@ -247,53 +239,24 @@
         }));
     }
 
-    @SmallTest
-    public void testNotificationShownForAlwaysOnApp() {
-        final Vpn vpn = spyVpn(primaryUser.id);
-        final InOrder order = inOrder(vpn);
-        setMockedUsers(primaryUser);
-
-        // Don't show a notification for regular disconnected states.
-        vpn.updateState(DetailedState.DISCONNECTED, TAG);
-        order.verify(vpn).updateAlwaysOnNotificationInternal(false);
-
-        // Start showing a notification for disconnected once always-on.
-        vpn.setAlwaysOnPackage(PKGS[0], false);
-        order.verify(vpn).updateAlwaysOnNotificationInternal(true);
-
-        // Stop showing the notification once connected.
-        vpn.updateState(DetailedState.CONNECTED, TAG);
-        order.verify(vpn).updateAlwaysOnNotificationInternal(false);
-
-        // Show the notification if we disconnect again.
-        vpn.updateState(DetailedState.DISCONNECTED, TAG);
-        order.verify(vpn).updateAlwaysOnNotificationInternal(true);
-
-        // Notification should be cleared after unsetting always-on package.
-        vpn.setAlwaysOnPackage(null, false);
-        order.verify(vpn).updateAlwaysOnNotificationInternal(false);
-    }
-
     /**
-     * Mock some methods of vpn object.
+     * A subclass of {@link Vpn} with some of the fields pre-mocked.
      */
-    private Vpn spyVpn(@UserIdInt int userId) {
-        final Vpn vpn = spy(new Vpn(Looper.myLooper(), mContext, mNetService, userId));
-
-        // Block calls to the NotificationManager or PendingIntent#getActivity.
-        doNothing().when(vpn).updateAlwaysOnNotificationInternal(anyBoolean());
-        return vpn;
-    }
-
-    private static void assertBlocked(Vpn vpn, int... uids) {
-        for (int uid : uids) {
-            assertTrue("Uid " + uid + " should be blocked", vpn.isBlockingUid(uid));
+    private class MockVpn extends Vpn {
+        public MockVpn(@UserIdInt int userId) {
+            super(Looper.myLooper(), mContext, mNetService, userId);
         }
-    }
 
-    private static void assertUnblocked(Vpn vpn, int... uids) {
-        for (int uid : uids) {
-            assertFalse("Uid " + uid + " should not be blocked", vpn.isBlockingUid(uid));
+        public void assertBlocked(int... uids) {
+            for (int uid : uids) {
+                assertTrue("Uid " + uid + " should be blocked", isBlockingUid(uid));
+            }
+        }
+
+        public void assertUnblocked(int... uids) {
+            for (int uid : uids) {
+                assertFalse("Uid " + uid + " should not be blocked", isBlockingUid(uid));
+            }
         }
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
similarity index 97%
rename from tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index 9f7261d..a30b362 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -59,14 +59,13 @@
     @Mock private INetworkStatsService mStatsService;
     @Mock private IControlsTethering mTetherHelper;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
-    @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices;
 
     private final TestLooper mLooper = new TestLooper();
     private TetherInterfaceStateMachine mTestedSm;
 
     private void initStateMachine(int interfaceType) throws Exception {
         mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType,
-                mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices);
+                mNMService, mStatsService, mTetherHelper);
         mTestedSm.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the test of the world that we've changed from an unknown to available state.
@@ -92,8 +91,7 @@
     @Test
     public void startsOutAvailable() {
         mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
-                ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper,
-                mIPv6TetheringInterfaceServices);
+                ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper);
         mTestedSm.start();
         mLooper.dispatchAll();
         verify(mTetherHelper).notifyInterfaceStateChange(
@@ -276,4 +274,4 @@
                 upstreamIface);
         mLooper.dispatchAll();
     }
-}
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
index 5eee7b9..21560ac 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.Mockito.when;
 
 import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
@@ -337,7 +336,7 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -345,7 +344,7 @@
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -375,7 +374,7 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -383,7 +382,7 @@
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -413,7 +412,7 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -421,7 +420,7 @@
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -451,17 +450,16 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
-                        ROAMING_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
-                        ROAMING_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES,
-                        2L, 0L);
+                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
                 VPN_INFO, TEST_START);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
index 728eb73..94c6711 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
@@ -22,9 +22,6 @@
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.METERED_ALL;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
@@ -43,21 +40,21 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
+import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
 
 import android.app.AlarmManager;
+import android.app.IAlarmListener;
+import android.app.IAlarmManager;
+import android.app.PendingIntent;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.content.Intent;
@@ -66,7 +63,6 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsSession;
 import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkState;
@@ -84,28 +80,23 @@
 import android.os.MessageQueue.IdleHandler;
 import android.os.Message;
 import android.os.PowerManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import android.os.WorkSource;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.util.TrustedTime;
 
 import com.android.internal.net.VpnInfo;
-import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.server.BroadcastInterceptingContext;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
 
 import libcore.io.IoUtils;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -115,11 +106,11 @@
 /**
  * Tests for {@link NetworkStatsService}.
  *
- * TODO: This test used to be really brittle because it used Easymock - it uses Mockito now, but
- * still uses the Easymock structure, which could be simplified.
+ * TODO: This test is really brittle, largely due to overly-strict use of Easymock.
+ * Rewrite w/ Mockito.
  */
-@RunWith(AndroidJUnit4.class)
-public class NetworkStatsServiceTest {
+@LargeTest
+public class NetworkStatsServiceTest extends AndroidTestCase {
     private static final String TAG = "NetworkStatsServiceTest";
 
     private static final String TEST_IFACE = "test0";
@@ -146,12 +137,10 @@
     private BroadcastInterceptingContext mServiceContext;
     private File mStatsDir;
 
-    private @Mock INetworkManagementService mNetManager;
-    private @Mock TrustedTime mTime;
-    private @Mock NetworkStatsSettings mSettings;
-    private @Mock IConnectivityManager mConnManager;
-    private @Mock IBinder mBinder;
-    private @Mock AlarmManager mAlarmManager;
+    private INetworkManagementService mNetManager;
+    private TrustedTime mTime;
+    private NetworkStatsSettings mSettings;
+    private IConnectivityManager mConnManager;
     private IdleableHandlerThread mHandlerThread;
     private Handler mHandler;
 
@@ -159,24 +148,32 @@
     private INetworkStatsSession mSession;
     private INetworkManagementEventObserver mNetworkObserver;
 
-    @Before
+    @Override
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        final Context context = InstrumentationRegistry.getContext();
+        super.setUp();
 
-        mServiceContext = new BroadcastInterceptingContext(context);
-        mStatsDir = context.getFilesDir();
+        mServiceContext = new BroadcastInterceptingContext(getContext());
+        mStatsDir = getContext().getFilesDir();
         if (mStatsDir.exists()) {
             IoUtils.deleteContents(mStatsDir);
         }
 
+        mNetManager = createMock(INetworkManagementService.class);
+
+        // TODO: Mock AlarmManager when migrating this test to Mockito.
+        AlarmManager alarmManager = (AlarmManager) mServiceContext
+                .getSystemService(Context.ALARM_SERVICE);
+        mTime = createMock(TrustedTime.class);
+        mSettings = createMock(NetworkStatsSettings.class);
+        mConnManager = createMock(IConnectivityManager.class);
+
         PowerManager powerManager = (PowerManager) mServiceContext.getSystemService(
                 Context.POWER_SERVICE);
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mService = new NetworkStatsService(
-                mServiceContext, mNetManager, mAlarmManager, wakeLock, mTime,
+                mServiceContext, mNetManager, alarmManager, wakeLock, mTime,
                 TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
                 mStatsDir, getBaseDir(mStatsDir));
         mHandlerThread = new IdleableHandlerThread("HandlerThread");
@@ -193,20 +190,22 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectSystemReady();
 
+        // catch INetworkManagementEventObserver during systemReady()
+        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
+                INetworkManagementEventObserver>();
+        mNetManager.registerObserver(capture(networkObserver));
+        expectLastCall().atLeastOnce();
+
+        replay();
         mService.systemReady();
         mSession = mService.openSession();
-        assertNotNull("openSession() failed", mSession);
+        verifyAndReset();
 
-
-        // catch INetworkManagementEventObserver during systemReady()
-        ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
-              ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
-        verify(mNetManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
     }
 
-    @After
+    @Override
     public void tearDown() throws Exception {
         IoUtils.deleteContents(mStatsDir);
 
@@ -220,9 +219,10 @@
 
         mSession.close();
         mService = null;
+
+        super.tearDown();
     }
 
-    @Test
     public void testNetworkStatsWifi() throws Exception {
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
@@ -231,13 +231,15 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
+        verifyAndReset();
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -246,11 +248,14 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
-
+        verifyAndReset();
 
         // and bump forward again, with counters going higher. this is
         // important, since polling should correctly subtract last snapshot.
@@ -260,14 +265,17 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
+        verifyAndReset();
 
     }
 
-    @Test
     public void testStatsRebootPersist() throws Exception {
         assertStatsFilesExist(false);
 
@@ -278,13 +286,15 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
+        verifyAndReset();
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -298,28 +308,33 @@
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+        expectNetworkStatsPoll();
+
         mService.setUidForeground(UID_RED, false);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 6);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
         assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
-                2L, 4);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
-                256L, 2L, 6);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_NO, 512L, 4L, 256L, 2L, 4);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_NO, 512L, 4L, 256L, 2L,
+                6);
         assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
-
+        verifyAndReset();
 
         // graceful shutdown system, which should trigger persist of stats, and
         // clear any values in memory.
         expectCurrentTime();
         expectDefaultSettings();
+        replay();
         mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
+        verifyAndReset();
+
         assertStatsFilesExist(true);
 
         // boot through serviceReady() again
@@ -328,22 +343,30 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectSystemReady();
 
+        // catch INetworkManagementEventObserver during systemReady()
+        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
+                INetworkManagementEventObserver>();
+        mNetManager.registerObserver(capture(networkObserver));
+        expectLastCall().atLeastOnce();
+
+        replay();
         mService.systemReady();
 
+        mNetworkObserver = networkObserver.getValue();
+
         // after systemReady(), we should have historical stats loaded again
         assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
         assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
-                2L, 4);
-        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
-                256L, 2L, 6);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_NO, 512L, 4L, 256L, 2L, 4);
+        assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_NO, 512L, 4L, 256L, 2L,
+                6);
         assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
+        verifyAndReset();
 
     }
 
     // TODO: simulate reboot to test bucket resize
-    @Test
-    @Ignore
+    @Suppress
     public void testStatsBucketResize() throws Exception {
         NetworkStatsHistory history = null;
 
@@ -356,10 +379,12 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -368,6 +393,9 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -375,7 +403,7 @@
         assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
         assertEquals(2, history.size());
-
+        verifyAndReset();
 
         // now change bucket duration setting and trigger another poll with
         // exact same values, which should resize existing buckets.
@@ -383,6 +411,9 @@
         expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify identical stats, but spread across 4 buckets now
@@ -390,10 +421,10 @@
         assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
         assertEquals(4, history.size());
+        verifyAndReset();
 
     }
 
-    @Test
     public void testUidStatsAcrossNetworks() throws Exception {
         // pretend first mobile network comes online
         expectCurrentTime();
@@ -401,10 +432,12 @@
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some traffic on first network
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -416,8 +449,11 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xF00D, 10);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -425,7 +461,7 @@
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 10);
         assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 0);
-
+        verifyAndReset();
 
         // now switch networks; this also tests that we're okay with interfaces
         // disappearing, to verify we don't count backwards.
@@ -439,11 +475,13 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
         forcePollAndWaitForIdle();
-
+        verifyAndReset();
 
         // create traffic on second network
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -456,8 +494,11 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify original history still intact
@@ -470,10 +511,10 @@
         assertNetworkTotal(sTemplateImsi2, 128L, 1L, 1024L, 8L, 0);
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1L, 1024L, 8L, 10);
+        verifyAndReset();
 
     }
 
-    @Test
     public void testUidRemovedIsMoved() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -481,10 +522,12 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -497,8 +540,11 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
                 .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -506,7 +552,7 @@
         assertUidTotal(sTemplateWifi, UID_RED, 16L, 1L, 16L, 1L, 10);
         assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 258L, 512L, 32L, 0);
         assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0);
-
+        verifyAndReset();
 
         // now pretend two UIDs are uninstalled, which should migrate stats to
         // special "removed" bucket.
@@ -519,6 +565,9 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
                 .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+        expectNetworkStatsPoll();
+
+        replay();
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_BLUE);
         mServiceContext.sendBroadcast(intent);
@@ -532,10 +581,10 @@
         assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L, 0L, 0L, 0);
         assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0);
         assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 259L, 528L, 33L, 10);
+        verifyAndReset();
 
     }
 
-    @Test
     public void testUid3g4gCombinedByTemplate() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -543,10 +592,12 @@
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -556,13 +607,16 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xF00D, 5);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
-
+        verifyAndReset();
 
         // now switch over to 4g network
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -573,11 +627,13 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
         forcePollAndWaitForIdle();
-
+        verifyAndReset();
 
         // create traffic on second network
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -589,15 +645,19 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
                 .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify that ALL_MOBILE template combines both
         assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
+
+        verifyAndReset();
     }
 
-    @Test
     public void testSummaryForAllUid() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -605,10 +665,12 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some traffic for two apps
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -619,14 +681,17 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 1);
         assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 8L, 512L, 4L, 0);
-
+        verifyAndReset();
 
         // now create more traffic in next hour, but only for one app
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -637,29 +702,33 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // first verify entire history present
         NetworkStats stats = mSession.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(3, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50L,
-                5L, 50L, 5L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 10L,
-                1L, 10L, 1L, 1);
-        assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                2048L, 16L, 1024L, 8L, 0);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 50L, 5L,
+                50L, 5L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 10L, 1L, 10L,
+                1L, 1);
+        assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2048L, 16L,
+                1024L, 8L, 0);
 
         // now verify that recent history only contains one uid
         final long currentTime = currentTimeMillis();
         stats = mSession.getSummaryForAllUid(
                 sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
         assertEquals(1, stats.size());
-        assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                1024L, 8L, 512L, 4L, 0);
+        assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
+                512L, 4L, 0);
+
+        verifyAndReset();
     }
 
-    @Test
     public void testForegroundBackground() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -667,10 +736,12 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some initial traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -680,13 +751,16 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+        expectNetworkStatsPoll();
+
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
-
+        verifyAndReset();
 
         // now switch to foreground
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -698,9 +772,12 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+        expectNetworkStatsPoll();
+
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
 
+        replay();
         forcePollAndWaitForIdle();
 
         // test that we combined correctly
@@ -710,59 +787,18 @@
         final NetworkStats stats = mSession.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(4, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
-                2L, 128L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
-                1L, 64L, 1L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                32L, 2L, 32L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, 1L,
-                1L, 1L, 1L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 2L,
+                128L, 2L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 1L, 64L,
+                1L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 2L,
+                32L, 2L, 1);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, ROAMING_NO, 1L, 1L, 1L,
+                1L, 1);
+
+        verifyAndReset();
     }
 
-    @Test
-    public void testMetered() throws Exception {
-        // pretend that network comes online
-        expectCurrentTime();
-        expectDefaultSettings();
-        expectNetworkState(buildWifiState(true /* isMetered */));
-        expectNetworkStatsSummary(buildEmptyStats());
-        expectNetworkStatsUidDetail(buildEmptyStats());
-        expectBandwidthControlCheck();
-
-        mService.forceUpdateIfaces();
-
-
-        // create some initial traffic
-        incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
-        expectDefaultSettings();
-        expectNetworkStatsSummary(buildEmptyStats());
-        // Note that all traffic from NetworkManagementService is tagged as METERED_NO and
-        // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
-        // on top by inspecting the iface properties.
-        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
-                        2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
-                        1L, 64L, 1L, 0L));
-        mService.incrementOperationCount(UID_RED, 0xF00D, 1);
-
-        forcePollAndWaitForIdle();
-
-        // verify service recorded history
-        assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
-        // verify entire history present
-        final NetworkStats stats = mSession.getSummaryForAllUid(
-                sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
-        assertEquals(2, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
-                128L, 2L, 128L, 2L, 1);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 64L,
-                1L, 64L, 1L, 1);
-    }
-
-    @Test
     public void testRoaming() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -770,24 +806,29 @@
         expectNetworkState(buildMobile3gState(IMSI_1, true /* isRoaming */));
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // Create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
-        // Note that all traffic from NetworkManagementService is tagged as METERED_NO and
-        // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
-        // on top by inspecting the iface properties.
+        // Note that all traffic from NetworkManagementService is tagged as ROAMING_NO, because
+        // roaming isn't tracked at that layer. We layer it on top by inspecting the iface
+        // properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
-                        128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, 64L,
-                        1L, 64L, 1L, 0L));
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 2L,
+                        128L, 2L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 1L, 64L,
+                        1L, 0L));
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -797,13 +838,14 @@
         final NetworkStats stats = mSession.getSummaryForAllUid(
                 sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(2, stats.size());
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES,
-                128L, 2L, 128L, 2L, 0);
-        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, 64L,
-                1L, 64L, 1L, 0);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 2L,
+                128L, 2L, 0);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_YES, 64L, 1L, 64L,
+                1L, 0);
+
+        verifyAndReset();
     }
 
-    @Test
     public void testTethering() throws Exception {
         // pretend first mobile network comes online
         expectCurrentTime();
@@ -811,10 +853,12 @@
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
-
+        verifyAndReset();
 
         // create some tethering traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
@@ -827,20 +871,22 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
         final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
         final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
-                        0L);
+                .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L);
 
         expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats);
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
         assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0);
         assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0);
+        verifyAndReset();
 
     }
 
-    @Test
     public void testRegisterUsageCallback() throws Exception {
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
@@ -849,12 +895,16 @@
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
         expectBandwidthControlCheck();
 
+        replay();
         mService.forceUpdateIfaces();
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        verifyAndReset();
+
         String callingPackage = "the.calling.package";
         long thresholdInBytes = 1L;  // very small; should be overriden by framework
         DataUsageRequest inputRequest = new DataUsageRequest(
@@ -865,18 +915,23 @@
         LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv);
         Messenger messenger = new Messenger(latchedHandler);
 
+        // Allow binder to connect
+        IBinder mockBinder = createMock(IBinder.class);
+        mockBinder.linkToDeath((IBinder.DeathRecipient) anyObject(), anyInt());
+        EasyMock.replay(mockBinder);
+
         // Force poll
         expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
-
-
+        expectNetworkStatsPoll();
+        replay();
 
         // Register and verify request and that binder was called
         DataUsageRequest request =
                 mService.registerUsageCallback(callingPackage, inputRequest,
-                        messenger, mBinder);
+                        messenger, mockBinder);
         assertTrue(request.requestId > 0);
         assertTrue(Objects.equals(sTemplateWifi, request.template));
         long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB
@@ -886,11 +941,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(-1));
         mHandlerThread.waitForIdle(WAIT_TIMEOUT);
 
-
+        verifyAndReset();
 
         // Make sure that the caller binder gets connected
-        verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
-
+        EasyMock.verify(mockBinder);
+        EasyMock.reset(mockBinder);
 
         // modify some number on wifi, and trigger poll event
         // not enough traffic to call data usage callback
@@ -900,9 +955,13 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
+        verifyAndReset();
         assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
 
         // make sure callback has not being called
@@ -916,11 +975,14 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
         forcePollAndWaitForIdle();
 
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 4096000L, 4L, 8192000L, 8L, 0);
-
+        verifyAndReset();
 
         // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
         assertTrue(cv.block(WAIT_TIMEOUT));
@@ -928,7 +990,9 @@
         cv.close();
 
         // Allow binder to disconnect
-        when(mBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt())).thenReturn(true);
+        expect(mockBinder.unlinkToDeath((IBinder.DeathRecipient) anyObject(), anyInt()))
+                .andReturn(true);
+        EasyMock.replay(mockBinder);
 
         // Unregister request
         mService.unregisterUsageRequest(request);
@@ -938,10 +1002,9 @@
         assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.mLastMessageType);
 
         // Make sure that the caller binder gets disconnected
-        verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+        EasyMock.verify(mockBinder);
     }
 
-    @Test
     public void testUnregisterUsageCallback_unknown_noop() throws Exception {
         String callingPackage = "the.calling.package";
         long thresholdInBytes = 10 * 1024 * 1024;  // 10 MB
@@ -971,18 +1034,18 @@
 
         // verify summary API
         final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
-        assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
-                rxBytes, rxPackets, txBytes, txPackets, operations);
+        assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, rxBytes,
+                rxPackets, txBytes, txPackets, operations);
     }
 
     private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
             long txBytes, long txPackets, int operations) throws Exception {
-        assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, rxBytes, rxPackets,
-                txBytes, txPackets, operations);
+        assertUidTotal(template, uid, SET_ALL, ROAMING_ALL, rxBytes, rxPackets, txBytes, txPackets,
+                operations);
     }
 
-    private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered,
-            int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations)
+    private void assertUidTotal(NetworkTemplate template, int uid, int set, int roaming,
+            long rxBytes, long rxPackets, long txBytes, long txPackets, int operations)
             throws Exception {
         // verify history API
         final NetworkStatsHistory history = mSession.getHistoryForUid(
@@ -993,35 +1056,38 @@
         // verify summary API
         final NetworkStats stats = mSession.getSummaryForAllUid(
                 template, Long.MIN_VALUE, Long.MAX_VALUE, false);
-        assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, rxBytes, rxPackets,
-                txBytes, txPackets, operations);
+        assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, roaming, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
     }
 
     private void expectSystemReady() throws Exception {
+        mNetManager.setGlobalAlert(anyLong());
+        expectLastCall().atLeastOnce();
+
         expectNetworkStatsSummary(buildEmptyStats());
         expectBandwidthControlCheck();
     }
 
     private void expectNetworkState(NetworkState... state) throws Exception {
-        when(mConnManager.getAllNetworkState()).thenReturn(state);
+        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
 
         final LinkProperties linkProp = state.length > 0 ? state[0].linkProperties : null;
-        when(mConnManager.getActiveLinkProperties()).thenReturn(linkProp);
+        expect(mConnManager.getActiveLinkProperties()).andReturn(linkProp).atLeastOnce();
     }
 
     private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
-        when(mConnManager.getAllVpnInfo()).thenReturn(new VpnInfo[0]);
+        expect(mConnManager.getAllVpnInfo()).andReturn(new VpnInfo[0]).atLeastOnce();
 
         expectNetworkStatsSummaryDev(summary);
         expectNetworkStatsSummaryXt(summary);
     }
 
     private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
-        when(mNetManager.getNetworkStatsSummaryDev()).thenReturn(summary);
+        expect(mNetManager.getNetworkStatsSummaryDev()).andReturn(summary).atLeastOnce();
     }
 
     private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception {
-        when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary);
+        expect(mNetManager.getNetworkStatsSummaryXt()).andReturn(summary).atLeastOnce();
     }
 
     private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
@@ -1031,10 +1097,11 @@
     private void expectNetworkStatsUidDetail(
             NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats)
             throws Exception {
-        when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail);
+        expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
 
         // also include tethering details, since they are folded into UID
-        when(mNetManager.getNetworkStatsTethering()).thenReturn(tetherStats);
+        expect(mNetManager.getNetworkStatsTethering())
+                .andReturn(tetherStats).atLeastOnce();
     }
 
     private void expectDefaultSettings() throws Exception {
@@ -1043,33 +1110,38 @@
 
     private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
             throws Exception {
-        when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
-        when(mSettings.getTimeCacheMaxAge()).thenReturn(DAY_IN_MILLIS);
-        when(mSettings.getSampleEnabled()).thenReturn(true);
+        expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
+        expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
+        expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes();
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
-        when(mSettings.getDevConfig()).thenReturn(config);
-        when(mSettings.getXtConfig()).thenReturn(config);
-        when(mSettings.getUidConfig()).thenReturn(config);
-        when(mSettings.getUidTagConfig()).thenReturn(config);
+        expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
+        expect(mSettings.getXtConfig()).andReturn(config).anyTimes();
+        expect(mSettings.getUidConfig()).andReturn(config).anyTimes();
+        expect(mSettings.getUidTagConfig()).andReturn(config).anyTimes();
 
-        when(mSettings.getGlobalAlertBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getDevPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getXtPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getUidPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
+        expect(mSettings.getGlobalAlertBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes();
+        expect(mSettings.getDevPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes();
+        expect(mSettings.getXtPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes();
+        expect(mSettings.getUidPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes();
+        expect(mSettings.getUidTagPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes();
     }
 
     private void expectCurrentTime() throws Exception {
-        when(mTime.forceRefresh()).thenReturn(false);
-        when(mTime.hasCache()).thenReturn(true);
-        when(mTime.currentTimeMillis()).thenReturn(currentTimeMillis());
-        when(mTime.getCacheAge()).thenReturn(0L);
-        when(mTime.getCacheCertainty()).thenReturn(0L);
+        expect(mTime.forceRefresh()).andReturn(false).anyTimes();
+        expect(mTime.hasCache()).andReturn(true).anyTimes();
+        expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
+        expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
+        expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
+    }
+
+    private void expectNetworkStatsPoll() throws Exception {
+        mNetManager.setGlobalAlert(anyLong());
+        expectLastCall().anyTimes();
     }
 
     private void expectBandwidthControlCheck() throws Exception {
-        when(mNetManager.isBandwidthControlEnabled()).thenReturn(true);
+        expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
     }
 
     private void assertStatsFilesExist(boolean exist) {
@@ -1082,8 +1154,8 @@
     }
 
     private static void assertValues(NetworkStats stats, String iface, int uid, int set,
-            int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
-            long txPackets, int operations) {
+            int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+            int operations) {
         final NetworkStats.Entry entry = new NetworkStats.Entry();
         List<Integer> sets = new ArrayList<>();
         if (set == SET_DEFAULT || set == SET_ALL) {
@@ -1101,21 +1173,11 @@
             roamings.add(ROAMING_YES);
         }
 
-        List<Integer> meterings = new ArrayList<>();
-        if (metered == METERED_NO || metered == METERED_ALL) {
-            meterings.add(METERED_NO);
-        }
-        if (metered == METERED_YES || metered == METERED_ALL) {
-            meterings.add(METERED_YES);
-        }
-
         for (int s : sets) {
             for (int r : roamings) {
-                for (int m : meterings) {
-                    final int i = stats.findIndex(iface, uid, s, tag, m, r);
-                    if (i != -1) {
-                        entry.add(stats.getValues(i, null));
-                    }
+                final int i = stats.findIndex(iface, uid, s, tag, r);
+                if (i != -1) {
+                    entry.add(stats.getValues(i, null));
                 }
             }
         }
@@ -1138,19 +1200,11 @@
     }
 
     private static NetworkState buildWifiState() {
-        return buildWifiState(false);
-    }
-
-    private static NetworkState buildWifiState(boolean isMetered) {
         final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
-        final NetworkCapabilities capabilities = new NetworkCapabilities();
-        if (!isMetered) {
-            capabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        }
-        return new NetworkState(info, prop, capabilities, null, null, TEST_SSID);
+        return new NetworkState(info, prop, null, null, null, TEST_SSID);
     }
 
     private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1164,8 +1218,7 @@
         info.setRoaming(isRoaming);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
-        final NetworkCapabilities capabilities = new NetworkCapabilities();
-        return new NetworkState(info, prop, capabilities, null, subscriberId, null);
+        return new NetworkState(info, prop, null, null, subscriberId, null);
     }
 
     private static NetworkState buildMobile4gState(String iface) {
@@ -1173,8 +1226,7 @@
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
-        final NetworkCapabilities capabilities = new NetworkCapabilities();
-        return new NetworkState(info, prop, capabilities, null, null, null);
+        return new NetworkState(info, prop, null, null, null, null);
     }
 
     private NetworkStats buildEmptyStats() {
@@ -1197,6 +1249,15 @@
         mElapsedRealtime += duration;
     }
 
+    private void replay() {
+        EasyMock.replay(mNetManager, mTime, mSettings, mConnManager);
+    }
+
+    private void verifyAndReset() {
+        EasyMock.verify(mNetManager, mTime, mSettings, mConnManager);
+        EasyMock.reset(mNetManager, mTime, mSettings, mConnManager);
+    }
+
     private void forcePollAndWaitForIdle() {
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
         // Send dummy message to make sure that any previous message has been handled
diff --git a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
new file mode 100644
index 0000000..2a7cbc2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+
+public class InstallerTest extends AndroidTestCase {
+    private static final String TAG = "InstallerTest";
+
+    private Installer mInstaller;
+
+    @Override
+    public void setUp() throws Exception {
+        mInstaller = new Installer(getContext());
+        mInstaller.onStart();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mInstaller = null;
+    }
+
+    public void testGetAppSize() throws Exception {
+        final PackageManager pm = getContext().getPackageManager();
+        for (ApplicationInfo app : pm.getInstalledApplications(0)) {
+            final int userId = UserHandle.getUserId(app.uid);
+            final int appId = UserHandle.getAppId(app.uid);
+
+            final String[] packageNames = pm.getPackagesForUid(app.uid);
+            final long[] ceDataInodes = new long[packageNames.length];
+            final String[] codePaths = new String[packageNames.length];
+
+            for (int i = 0; i < packageNames.length; i++) {
+                final ApplicationInfo info = pm.getApplicationInfo(packageNames[i], 0);
+                codePaths[i] = info.getCodePath();
+            }
+
+            final PackageStats stats = new PackageStats(app.packageName);
+            final PackageStats quotaStats = new PackageStats(app.packageName);
+
+            mInstaller.getAppSize(app.volumeUuid, packageNames, userId, 0,
+                    appId, ceDataInodes, codePaths, stats);
+
+            mInstaller.getAppSize(app.volumeUuid, packageNames, userId, Installer.FLAG_USE_QUOTA,
+                    appId, ceDataInodes, codePaths, quotaStats);
+
+            checkEquals(Arrays.toString(packageNames) + " UID=" + app.uid, stats, quotaStats);
+        }
+    }
+
+    public void testGetUserSize() throws Exception {
+        int[] appIds = null;
+
+        final PackageManager pm = getContext().getPackageManager();
+        for (ApplicationInfo app : pm.getInstalledApplications(0)) {
+            final int appId = UserHandle.getAppId(app.uid);
+            if (!ArrayUtils.contains(appIds, appId)) {
+                appIds = ArrayUtils.appendInt(appIds, appId);
+            }
+        }
+
+        final PackageStats stats = new PackageStats("android");
+        final PackageStats quotaStats = new PackageStats("android");
+
+        mInstaller.getUserSize(null, UserHandle.USER_SYSTEM, 0,
+                appIds, stats);
+
+        mInstaller.getUserSize(null, UserHandle.USER_SYSTEM, Installer.FLAG_USE_QUOTA,
+                appIds, quotaStats);
+
+        checkEquals(Arrays.toString(appIds), stats, quotaStats);
+    }
+
+    public void testGetExternalSize() throws Exception {
+
+        final long[] stats = mInstaller.getExternalSize(null, UserHandle.USER_SYSTEM, 0);
+
+        final long[] quotaStats = mInstaller.getExternalSize(null, UserHandle.USER_SYSTEM,
+                Installer.FLAG_USE_QUOTA);
+
+        for (int i = 0; i < stats.length; i++) {
+            checkEquals("#" + i, stats[i], quotaStats[i]);
+        }
+    }
+
+    private static void checkEquals(String msg, PackageStats a, PackageStats b) {
+        checkEquals(msg + " codeSize", a.codeSize, b.codeSize);
+        checkEquals(msg + " dataSize", a.dataSize, b.dataSize);
+        checkEquals(msg + " cacheSize", a.cacheSize, b.cacheSize);
+        checkEquals(msg + " externalCodeSize", a.externalCodeSize, b.externalCodeSize);
+        checkEquals(msg + " externalDataSize", a.externalDataSize, b.externalDataSize);
+        checkEquals(msg + " externalCacheSize", a.externalCacheSize, b.externalCacheSize);
+    }
+
+    private static void checkEquals(String msg, long expected, long actual) {
+        if (expected != actual) {
+            Log.e(TAG, msg + " expected " + expected + " actual " + actual);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 051f1bd..4565dbd 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -146,7 +146,6 @@
     /**
      * Control whether users receive a simplified network settings UI and improved network
      * selection.
-     * @hide
      */
     public static final String
             KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
@@ -1035,7 +1034,7 @@
      * is returned.
      * @hide
      */
-    public static final String FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array";
+    public static final String KEY_FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array";
 
     /**
      * Determine whether user can change Wi-Fi Calling preference in roaming.
@@ -1237,7 +1236,7 @@
         sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
         sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
-        sDefaults.putStringArray(FILTERED_CNAP_NAMES_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
     }
 
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 0334254..811c996 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -233,6 +233,13 @@
      */
     public static final int DIALED_ON_WRONG_SLOT = 56;
 
+    /**
+     * The network does not accept the emergency call request because IMEI was used as
+     * identification and this cability is not supported by the network.
+     * {@hide}
+     */
+    public static final int IMEI_NOT_ACCEPTED = 57;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Please assign the new type the next id value below.
@@ -241,14 +248,14 @@
     // 4) Update toString() with the newly added disconnect type.
     // 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
     //
-    // NextId: 57
+    // NextId: 58
     //*********************************************************************************************
 
     /** Smallest valid value for call disconnect codes. */
     public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
 
     /** Largest valid value for call disconnect codes. */
-    public static final int MAXIMUM_VALID_VALUE = DIALED_ON_WRONG_SLOT;
+    public static final int MAXIMUM_VALID_VALUE = IMEI_NOT_ACCEPTED;
 
     /** Private constructor to avoid class instantiation. */
     private DisconnectCause() {
@@ -370,6 +377,8 @@
             return "DATA_LIMIT_REACHED";
         case DIALED_ON_WRONG_SLOT:
             return "DIALED_ON_WRONG_SLOT";
+        case IMEI_NOT_ACCEPTED:
+            return "IMEI_NOT_ACCEPTED";
         default:
             return "INVALID: " + cause;
         }
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
deleted file mode 100644
index 8aa27a9..0000000
--- a/tests/net/Android.mk
+++ /dev/null
@@ -1,80 +0,0 @@
-#########################################################################
-# Build FrameworksNetTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    framework-protos \
-    android-support-test \
-    mockito-target-minus-junit4 \
-    platform-test-annotations \
-    services.core \
-    services.net
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.runner
-
-LOCAL_PACKAGE_NAME := FrameworksNetTests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
-    libbacktrace \
-    libbase \
-    libbinder \
-    libc++ \
-    libcutils \
-    liblog \
-    liblzma \
-    libnativehelper \
-    libnetdaidl \
-    libui \
-    libunwind \
-    libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Wall -Wextra -Werror
-
-LOCAL_C_INCLUDES := \
-  libpcap \
-  hardware/google/apf
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under)
-
-LOCAL_SHARED_LIBRARIES := \
-  libbinder \
-  liblog \
-  libcutils \
-  libnativehelper \
-  libnetdaidl
-
-LOCAL_STATIC_LIBRARIES := \
-  libpcap \
-  libapf
-
-LOCAL_MODULE := libframeworksnettestsjni
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
deleted file mode 100644
index e069dd0..0000000
--- a/tests/net/AndroidManifest.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.tests.net">
-
-    <uses-permission android:name="android.permission.READ_LOGS" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
-    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
-    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
-    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.MANAGE_USERS" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
-    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
-    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
-    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
-    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.frameworks.tests.net"
-        android:label="Frameworks Networking Tests" />
-</manifest>
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index acbe4bc..d53167f 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -27,6 +27,4 @@
     android-support-test \
     mockito-target
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 9e897bf..18c1245 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -59,7 +59,7 @@
 
     int addOrUpdateNetwork(in WifiConfiguration config);
 
-    boolean addPasspointConfiguration(in PasspointConfiguration config);
+    boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config);
 
     boolean removePasspointConfiguration(in String fqdn);
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3b7f721..958279b 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -608,6 +608,7 @@
      * if there has been a report of it having no internet access, and, it never have had
      * internet access in the past.
      */
+    @SystemApi
     public boolean hasNoInternetAccess() {
         return numNoInternetAccessReports > 0 && !validatedInternetAccess;
     }
@@ -621,6 +622,17 @@
     public boolean noInternetAccessExpected;
 
     /**
+     * The WiFi configuration is expected not to have Internet access (e.g., a wireless printer, a
+     * Chromecast hotspot, etc.). This will be set if the user explicitly confirms a connection to
+     * this configuration and selects "don't ask again".
+     * @hide
+     */
+    @SystemApi
+    public boolean isNoInternetAccessExpected() {
+        return noInternetAccessExpected;
+    }
+
+    /**
      * @hide
      * Last time the system was connected to this configuration.
      */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 674c161..618a719 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -842,31 +842,31 @@
     }
 
     /**
-     * Add a Passpoint configuration.  The configuration provides a credential
+     * Add or update a Passpoint configuration.  The configuration provides a credential
      * for connecting to Passpoint networks that are operated by the Passpoint
      * service provider specified in the configuration.
      *
      * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain
-     * Name).  In the case when there is an existing configuration with the same base
-     * domain, the new configuration will replace the existing configuration.
+     * Name).  In the case when there is an existing configuration with the same
+     * FQDN, the new configuration will replace the existing configuration.
      *
      * @param config The Passpoint configuration to be added
-     * @return true on success or false on failure
+     * @return true on success
      * @hide
      */
-    public boolean addPasspointConfiguration(PasspointConfiguration config) {
+    public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            return mService.addPasspointConfiguration(config);
+            return mService.addOrUpdatePasspointConfiguration(config);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Remove a Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+     * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
      *
      * @param fqdn The FQDN of the passpoint configuration to be removed
-     * @return true on success or false on failure
+     * @return true on success
      * @hide
      */
     public boolean removePasspointConfiguration(String fqdn) {
@@ -880,7 +880,9 @@
     /**
      * Return the list of installed Passpoint configurations.
      *
-     * @return A list of PasspointConfiguration or null
+     * An empty list will be returned when no configurations are installed.
+     *
+     * @return A list of {@link PasspointConfiguration}
      * @hide
      */
     public List<PasspointConfiguration> getPasspointConfigurations() {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java b/wifi/java/android/net/wifi/aware/AttachCallback.java
similarity index 78%
rename from wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
rename to wifi/java/android/net/wifi/aware/AttachCallback.java
index 1e8dbd9..90216f3 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
+++ b/wifi/java/android/net/wifi/aware/AttachCallback.java
@@ -18,16 +18,16 @@
 
 /**
  * Base class for Aware attach callbacks. Should be extended by applications and set when calling
- * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}. These are callbacks
+ * {@link WifiAwareManager#attach(AttachCallback, android.os.Handler)}. These are callbacks
  * applying to the Aware connection as a whole - not to specific publish or subscribe sessions -
- * for that see {@link WifiAwareDiscoverySessionCallback}.
+ * for that see {@link DiscoverySessionCallback}.
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwareAttachCallback {
+public class AttachCallback {
     /**
      * Called when Aware attach operation
-     * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}
+     * {@link WifiAwareManager#attach(AttachCallback, android.os.Handler)}
      * is completed and that we can now start discovery sessions or connections.
      *
      * @param session The Aware object on which we can execute further Aware operations - e.g.
@@ -39,7 +39,7 @@
 
     /**
      * Called when Aware attach operation
-     * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)} failed.
+     * {@link WifiAwareManager#attach(AttachCallback, android.os.Handler)} failed.
      */
     public void onAttachFailed() {
         /* empty */
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl b/wifi/java/android/net/wifi/aware/Characteristics.aidl
similarity index 94%
rename from wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
rename to wifi/java/android/net/wifi/aware/Characteristics.aidl
index a35e71d..77305e9 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
+++ b/wifi/java/android/net/wifi/aware/Characteristics.aidl
@@ -16,4 +16,4 @@
 
 package android.net.wifi.aware;
 
-parcelable WifiAwareCharacteristics;
+parcelable Characteristics;
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java
similarity index 83%
rename from wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
rename to wifi/java/android/net/wifi/aware/Characteristics.java
index 092aa34..1c3e390 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
+++ b/wifi/java/android/net/wifi/aware/Characteristics.java
@@ -25,7 +25,7 @@
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwareCharacteristics implements Parcelable {
+public class Characteristics implements Parcelable {
     /** @hide */
     public static final String KEY_MAX_SERVICE_NAME_LENGTH = "key_max_service_name_length";
     /** @hide */
@@ -37,7 +37,7 @@
     private Bundle mCharacteristics = new Bundle();
 
     /** @hide : should not be created by apps */
-    public WifiAwareCharacteristics(Bundle characteristics) {
+    public Characteristics(Bundle characteristics) {
         mCharacteristics = characteristics;
     }
 
@@ -58,7 +58,7 @@
      * message exchange. Restricts the parameters of the
      * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])},
      * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, and
-     * {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int, byte[])}
+     * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
      * variants.
      *
      * @return A positive integer, maximum length of byte array for Aware messaging.
@@ -89,17 +89,17 @@
         return 0;
     }
 
-    public static final Creator<WifiAwareCharacteristics> CREATOR =
-            new Creator<WifiAwareCharacteristics>() {
+    public static final Creator<Characteristics> CREATOR =
+            new Creator<Characteristics>() {
                 @Override
-                public WifiAwareCharacteristics createFromParcel(Parcel in) {
-                    WifiAwareCharacteristics c = new WifiAwareCharacteristics(in.readBundle());
+                public Characteristics createFromParcel(Parcel in) {
+                    Characteristics c = new Characteristics(in.readBundle());
                     return c;
                 }
 
                 @Override
-                public WifiAwareCharacteristics[] newArray(int size) {
-                    return new WifiAwareCharacteristics[size];
+                public Characteristics[] newArray(int size) {
+                    return new Characteristics[size];
                 }
             };
 }
diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
index 4b21b15..6a5957b 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(WifiAwareAttachCallback, android.os.Handler)}.
+ * {@link WifiAwareManager#attach(AttachCallback, 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/WifiAwareDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
similarity index 79%
rename from wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
rename to wifi/java/android/net/wifi/aware/DiscoverySession.java
index 2812ad4..c89718f 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -29,21 +29,21 @@
 /**
  * A class representing a single publish or subscribe Aware session. This object
  * will not be created directly - only its child classes are available:
- * {@link WifiAwarePublishDiscoverySession} and {@link WifiAwareSubscribeDiscoverySession}. This
+ * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This
  * class provides functionality common to both publish and subscribe discovery sessions:
  * <ul>
- *     <li>Sending messages: {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[])} or
- *     {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)} methods.
+ *     <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} or
+ *     {@link #sendMessage(PeerHandle, int, byte[], int)} methods.
  *     <li>Creating a network-specifier when requesting a Aware connection:
- *     {@link #createNetworkSpecifier(WifiAwareManager.PeerHandle, byte[])}.
+ *     {@link #createNetworkSpecifier(PeerHandle, byte[])}.
  * </ul>
  * The {@link #destroy()} method must be called to destroy discovery sessions once they are
  * no longer needed.
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwareDiscoveryBaseSession {
-    private static final String TAG = "WifiAwareDiscBaseSsn";
+public class DiscoverySession {
+    private static final String TAG = "DiscoverySession";
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
@@ -62,7 +62,7 @@
 
     /**
      * Return the maximum permitted retry count when sending messages using
-     * {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)}.
+     * {@link #sendMessage(PeerHandle, int, byte[], int)}.
      *
      * @return Maximum retry count when sending messages.
      */
@@ -71,7 +71,7 @@
     }
 
     /** @hide */
-    public WifiAwareDiscoveryBaseSession(WifiAwareManager manager, int clientId, int sessionId) {
+    public DiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
         if (VDBG) {
             Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId="
                     + clientId + ", sessionId=" + sessionId);
@@ -93,7 +93,7 @@
      *     This operation must be done on a session which is no longer needed. Otherwise system
      *     resources will continue to be utilized until the application exits. The only
      *     exception is a session for which we received a termination callback,
-     *     {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}.
+     *     {@link DiscoverySessionCallback#onSessionTerminated()}.
      */
     public void destroy() {
         WifiAwareManager mgr = mMgr.get();
@@ -139,23 +139,23 @@
     /**
      * 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,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
-     *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
-     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
+     *     {@link DiscoverySessionCallback} indicate message was transmitted successfully,
+     *     {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
      *     failed (possibly after several retries) -
-     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
+     *     {@link DiscoverySessionCallback#onMessageSendFailed(int)}.
      * <p>
      *     The peer will get a callback indicating a message was received using
-     *     {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     *     {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      *     byte[])}.
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
-     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicating message send success or
@@ -167,7 +167,7 @@
      *            (note: no retransmissions are attempted in other failure cases). A value of 0
      *            indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
      */
-    public void sendMessage(@NonNull WifiAwareManager.PeerHandle peerHandle, int messageId,
+    public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
             @Nullable byte[] message, int retryCount) {
         if (mTerminated) {
             Log.w(TAG, "sendMessage: called on terminated session");
@@ -186,25 +186,25 @@
     /**
      * 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,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
-     *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
-     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
+     *     {@link DiscoverySessionCallback} indicate message was transmitted successfully,
+     *     {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
      *     failed (possibly after several retries) -
-     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
+     *     {@link DiscoverySessionCallback#onMessageSendFailed(int)}.
      * <p>
      * The peer will get a callback indicating a message was received using
-     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}.
-     * Equivalent to {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)}
+     * Equivalent to {@link #sendMessage(PeerHandle, int, byte[], int)}
      * with a {@code retryCount} of 0.
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
-     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicating message send success or
@@ -212,16 +212,16 @@
      *                  can be arbitrary and non-unique.
      * @param message The message to be transmitted.
      */
-    public void sendMessage(@NonNull WifiAwareManager.PeerHandle peerHandle, int messageId,
+    public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
             @Nullable byte[] message) {
         sendMessage(peerHandle, messageId, message, 0);
     }
 
     /**
      * Start a ranging operation with the specified peers. The peer IDs are obtained from an
-     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])} operation - can
      * only range devices which are part of an ongoing discovery session.
      *
@@ -265,9 +265,9 @@
      * and a Publisher is a RESPONDER.
      *
      * @param peerHandle The peer's handle obtained through
-     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
      * byte[], java.util.List)} or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+     * {@link DiscoverySessionCallback#onMessageReceived(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
      *                   it will accept connection requests from any device.
@@ -283,7 +283,7 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifier(@Nullable WifiAwareManager.PeerHandle peerHandle,
+    public String createNetworkSpecifier(@Nullable PeerHandle peerHandle,
             @Nullable byte[] token) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifier: called on terminated session");
@@ -295,7 +295,7 @@
                 return null;
             }
 
-            int role = this instanceof WifiAwareSubscribeDiscoverySession
+            int role = this instanceof SubscribeDiscoverySession
                     ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                     : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
 
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
new file mode 100644
index 0000000..1fe449f
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -0,0 +1,156 @@
+/*
+ * 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.NonNull;
+
+import java.util.List;
+
+/**
+ * Base class for Aware session events callbacks. Should be extended by
+ * applications wanting notifications. The callbacks are set when a
+ * publish or subscribe session is created using
+ * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
+ * android.os.Handler)} or
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
+ * android.os.Handler)}.
+ * <p>
+ * A single callback is set at session creation - it cannot be replaced.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class DiscoverySessionCallback {
+    /**
+     * Called when a publish operation is started successfully in response to a
+     * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
+     * android.os.Handler)} operation.
+     *
+     * @param session The {@link PublishDiscoverySession} used to control the
+     *            discovery session.
+     */
+    public void onPublishStarted(@NonNull PublishDiscoverySession session) {
+        /* empty */
+    }
+
+    /**
+     * Called when a subscribe operation is started successfully in response to a
+     * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
+     * android.os.Handler)} operation.
+     *
+     * @param session The {@link SubscribeDiscoverySession} used to control the
+     *            discovery session.
+     */
+    public void onSubscribeStarted(@NonNull SubscribeDiscoverySession session) {
+        /* empty */
+    }
+
+    /**
+     * Called when a publish or subscribe discovery session configuration update request
+     * succeeds. Called in response to
+     * {@link PublishDiscoverySession#updatePublish(PublishConfig)} or
+     * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     */
+    public void onSessionConfigUpdated() {
+        /* empty */
+    }
+
+    /**
+     * Called when a publish or subscribe discovery session cannot be created:
+     * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
+     * android.os.Handler)} or
+     * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
+     * android.os.Handler)}, or when a configuration update fails:
+     * {@link PublishDiscoverySession#updatePublish(PublishConfig)} or
+     * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     * <p>
+     *     For discovery session updates failure leaves the session running with its previous
+     *     configuration - the discovery session is not terminated.
+     */
+    public void onSessionConfigFailed() {
+        /* empty */
+    }
+
+    /**
+     * Called when a discovery session (publish or subscribe) terminates. Termination may be due
+     * to user-request (either directly through {@link DiscoverySession#destroy()} or
+     * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
+     * or {@link SubscribeConfig.Builder#setTtlSec(int)}).
+     */
+    public void onSessionTerminated() {
+        /* empty */
+    }
+
+    /**
+     * Called when a discovery (publish or subscribe) operation results in a
+     * service discovery.
+     *
+     * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param serviceSpecificInfo The service specific information (arbitrary
+     *            byte array) provided by the peer as part of its discovery
+     *            configuration.
+     * @param matchFilter The filter which resulted in this service discovery.
+     */
+    public void onServiceDiscovered(PeerHandle peerHandle,
+            byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
+        /* empty */
+    }
+
+    /**
+     * Called in response to
+     * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
+     * when a message is transmitted successfully - i.e. when it was received successfully by the
+     * peer (corresponds to an ACK being received).
+     * <p>
+     * Note that either this callback or
+     * {@link DiscoverySessionCallback#onMessageSendFailed(int)} will be
+     * received - never both.
+     *
+     * @param messageId The arbitrary message ID specified when sending the message.
+     */
+    public void onMessageSendSucceeded(@SuppressWarnings("unused") int messageId) {
+        /* empty */
+    }
+
+    /**
+     * Called when message transmission fails - when no ACK is received from the peer.
+     * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
+     * the {@link DiscoverySession#sendMessage(PeerHandle, int,
+     * byte[], int)} method) - this event is received after all retries are exhausted.
+     * <p>
+     * Note that either this callback or
+     * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)} will be received
+     * - never both.
+     *
+     * @param messageId The arbitrary message ID specified when sending the message.
+     */
+    public void onMessageSendFailed(@SuppressWarnings("unused") int messageId) {
+        /* empty */
+    }
+
+    /**
+     * Called when a message is received from a discovery session peer - in response to the
+     * peer's {@link DiscoverySession#sendMessage(PeerHandle, int,
+     * byte[])} or {@link DiscoverySession#sendMessage(PeerHandle,
+     * int, byte[], int)}.
+     *
+     * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param message A byte array containing the message.
+     */
+    public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
+        /* empty */
+    }
+}
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 9c92807..794c142 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -23,7 +23,7 @@
 import android.net.wifi.aware.IWifiAwareEventCallback;
 import android.net.wifi.aware.PublishConfig;
 import android.net.wifi.aware.SubscribeConfig;
-import android.net.wifi.aware.WifiAwareCharacteristics;
+import android.net.wifi.aware.Characteristics;
 import android.net.wifi.RttManager;
 
 /**
@@ -37,7 +37,7 @@
     void enableUsage();
     void disableUsage();
     boolean isUsageEnabled();
-    WifiAwareCharacteristics getCharacteristics();
+    Characteristics getCharacteristics();
 
     // client API
     void connect(in IBinder binder, in String callingPackage, in IWifiAwareEventCallback callback,
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
similarity index 97%
rename from wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
rename to wifi/java/android/net/wifi/aware/IdentityChangedListener.java
index e8f52cd4..b0f97bd 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
+++ b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
@@ -28,7 +28,7 @@
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwareIdentityChangedListener {
+public class IdentityChangedListener {
     /**
      * @param mac The MAC address of the Aware discovery interface. The application must have the
      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to get the actual MAC address,
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
new file mode 100644
index 0000000..777d9a3
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}, used
+ * when sending messages e,g, {@link PublishDiscoverySession#sendMessage(PeerHandle, int, byte[])},
+ * or when configuring a network link to a peer, e.g.
+ * {@link PublishDiscoverySession#createNetworkSpecifier(PeerHandle, byte[])}.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class PeerHandle {
+    /** @hide */
+    public PeerHandle(int peerId) {
+        this.peerId = peerId;
+    }
+
+    /** @hide */
+    public int peerId;
+}
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index 3925bd7..1b8ef84 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -33,9 +33,9 @@
 /**
  * Defines the configuration of a Aware publish session. Built using
  * {@link PublishConfig.Builder}. A publish session is created using
- * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
  * android.os.Handler)} or updated using
- * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
+ * {@link PublishDiscoverySession#updatePublish(PublishConfig)}.
  *
  * @hide PROPOSED_AWARE_API
  */
@@ -184,7 +184,7 @@
      *
      * @hide
      */
-    public void assertValid(WifiAwareCharacteristics characteristics)
+    public void assertValid(Characteristics characteristics)
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
@@ -322,12 +322,11 @@
          * Sets the number of times an unsolicited (configured using
          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
          * will be broadcast. When the count is reached an event will be
-         * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
-         * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
-         * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
+         * generated for {@link DiscoverySessionCallback#onSessionTerminated()}
+         * [unless {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link DiscoverySession#destroy()} is
          *     called.
          *
          * @param publishCount Number of publish packets to broadcast.
@@ -348,12 +347,11 @@
          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
          * will be alive - broadcasting a packet. When the TTL is reached
          * an event will be generated for
-         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
-         * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}  [unless
+         * {@link DiscoverySessionCallback#onSessionTerminated()} [unless
          * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link DiscoverySession#destroy()} is
          *     called.
          *
          * @param ttlSec Lifetime of a publish session in seconds.
@@ -371,7 +369,7 @@
 
         /**
          * Configure whether a publish terminate notification
-         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
+         * {@link DiscoverySessionCallback#onSessionTerminated()} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java b/wifi/java/android/net/wifi/aware/PublishDiscoverySession.java
similarity index 70%
rename from wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/PublishDiscoverySession.java
index 68786d1..f2355b3 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/PublishDiscoverySession.java
@@ -21,32 +21,32 @@
 
 /**
  * A class representing a Aware publish session. Created when
- * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
  * android.os.Handler)} is called and a discovery session is created and returned in
- * {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}. See
- * baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}. This
+ * {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)}. See
+ * baseline functionality of all discovery sessions in {@link DiscoverySession}. This
  * object allows updating an existing/running publish discovery session using
  * {@link #updatePublish(PublishConfig)}.
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwarePublishDiscoverySession extends WifiAwareDiscoveryBaseSession {
-    private static final String TAG = "WifiAwarePublishDiscSsn";
+public class PublishDiscoverySession extends DiscoverySession {
+    private static final String TAG = "PublishDiscoverySession";
 
     /** @hide */
-    public WifiAwarePublishDiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
+    public PublishDiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
         super(manager, clientId, sessionId);
     }
 
     /**
      * Re-configure the currently active publish session. The
-     * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
+     * {@link DiscoverySessionCallback} is not replaced - the same listener used
      * at creation is still used. The results of the configuration are returned using
-     * {@link WifiAwareDiscoverySessionCallback}:
+     * {@link DiscoverySessionCallback}:
      * <ul>
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigUpdated()}: configuration
      *     update succeeded.
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigFailed()}: configuration
      *     update failed. The publish discovery session is still running using its previous
      *     configuration (i.e. update failure does not terminate the session).
      * </ul>
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 0fe69a8..a54a4f5 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -33,9 +33,9 @@
 /**
  * Defines the configuration of a Aware subscribe session. Built using
  * {@link SubscribeConfig.Builder}. Subscribe is done using
- * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
  * android.os.Handler)} or
- * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
  *
  * @hide PROPOSED_AWARE_API
  */
@@ -212,7 +212,7 @@
      *
      * @hide
      */
-    public void assertValid(WifiAwareCharacteristics characteristics)
+    public void assertValid(Characteristics characteristics)
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
@@ -355,11 +355,10 @@
          * Sets the number of times an active (
          * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
          * will broadcast. When the count is reached an event will be
-         * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
-         * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+         * generated for {@link DiscoverySessionCallback#onSessionTerminated()}.
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link DiscoverySession#destroy()} is
          *     called.
          *
          * @param subscribeCount Number of subscribe packets to broadcast.
@@ -380,11 +379,10 @@
          * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
          * will be alive - i.e. broadcasting a packet. When the TTL is reached
          * an event will be generated for
-         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
-         * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+         * {@link DiscoverySessionCallback#onSessionTerminated()}.
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link DiscoverySession#destroy()} is
          *     called.
          *
          * @param ttlSec Lifetime of a subscribe session in seconds.
@@ -404,8 +402,8 @@
          * Sets the match style of the subscription - how are matches from a
          * single match session (corresponding to the same publish action on the
          * peer) reported to the host (using the
-         * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-         * byte[], List)}). The options are: only report the first match and ignore the rest
+         * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[],
+         * java.util.List)}). The options are: only report the first match and ignore the rest
          * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single
          * match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default).
          *
@@ -424,7 +422,7 @@
 
         /**
          * Configure whether a subscribe terminate notification
-         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
+         * {@link DiscoverySessionCallback#onSessionTerminated()} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java b/wifi/java/android/net/wifi/aware/SubscribeDiscoverySession.java
similarity index 75%
rename from wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/SubscribeDiscoverySession.java
index a0ec809..39db1c7 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeDiscoverySession.java
@@ -22,35 +22,35 @@
 /**
  * A class representing a Aware subscribe session. Created when
  * {@link WifiAwareSession#subscribe(SubscribeConfig,
- * WifiAwareDiscoverySessionCallback, android.os.Handler)}
+ * DiscoverySessionCallback, android.os.Handler)}
  * is called and a discovery session is created and returned in
- * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(WifiAwareSubscribeDiscoverySession)}.
- * See baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}.
+ * {@link DiscoverySessionCallback#onSubscribeStarted(SubscribeDiscoverySession)}.
+ * See baseline functionality of all discovery sessions in {@link DiscoverySession}.
  * This object allows updating an existing/running subscribe discovery session using
  * {@link #updateSubscribe(SubscribeConfig)}.
  *
  * @hide PROPOSED_AWARE_API
  */
-public class WifiAwareSubscribeDiscoverySession extends WifiAwareDiscoveryBaseSession {
-    private static final String TAG = "WifiAwareSubsDiscSsn";
+public class SubscribeDiscoverySession extends DiscoverySession {
+    private static final String TAG = "SubscribeDiscSession";
 
     /**
      * {@hide}
      */
-    public WifiAwareSubscribeDiscoverySession(WifiAwareManager manager, int clientId,
+    public SubscribeDiscoverySession(WifiAwareManager manager, int clientId,
             int sessionId) {
         super(manager, clientId, sessionId);
     }
 
     /**
      * Re-configure the currently active subscribe session. The
-     * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
+     * {@link DiscoverySessionCallback} is not replaced - the same listener used
      * at creation is still used. The results of the configuration are returned using
-     * {@link WifiAwareDiscoverySessionCallback}:
+     * {@link DiscoverySessionCallback}:
      * <ul>
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigUpdated()}: configuration
      *     update succeeded.
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigFailed()}: configuration
      *     update failed. The subscribe discovery session is still running using its previous
      *     configuration (i.e. update failure does not terminate the session).
      * </ul>
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
deleted file mode 100644
index fdf0d01..0000000
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
+++ /dev/null
@@ -1,185 +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.IntDef;
-import android.annotation.NonNull;
-
-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
- * applications wanting notifications. The callbacks are set when a
- * publish or subscribe session is created using
- * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
- * android.os.Handler)} or
- * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
- * android.os.Handler)}.
- * <p>
- * A single callback is set at session creation - it cannot be replaced.
- *
- * @hide PROPOSED_AWARE_API
- */
-public class WifiAwareDiscoverySessionCallback {
-    /** @hide */
-    @IntDef({
-            TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SessionTerminateCodes {
-    }
-
-    /**
-     * Indicates that publish or subscribe session is done - all the
-     * requested operations (per {@link PublishConfig} or
-     * {@link SubscribeConfig}) have been executed. Failure reason flag for
-     * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
-     */
-    public static final int TERMINATE_REASON_DONE = 100;
-
-    /**
-     * Indicates that publish or subscribe session is terminated due to a
-     * failure.
-     * Failure reason flag for
-     * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
-     */
-    public static final int TERMINATE_REASON_FAIL = 101;
-
-    /**
-     * Called when a publish operation is started successfully in response to a
-     * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
-     * android.os.Handler)} operation.
-     *
-     * @param session The {@link WifiAwarePublishDiscoverySession} used to control the
-     *            discovery session.
-     */
-    public void onPublishStarted(@NonNull WifiAwarePublishDiscoverySession session) {
-        /* empty */
-    }
-
-    /**
-     * Called when a subscribe operation is started successfully in response to a
-     * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
-     * android.os.Handler)} operation.
-     *
-     * @param session The {@link WifiAwareSubscribeDiscoverySession} used to control the
-     *            discovery session.
-     */
-    public void onSubscribeStarted(@NonNull WifiAwareSubscribeDiscoverySession session) {
-        /* empty */
-    }
-
-    /**
-     * Called when a publish or subscribe discovery session configuration update request
-     * succeeds. Called in response to
-     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
-     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
-     */
-    public void onSessionConfigUpdated() {
-        /* empty */
-    }
-
-    /**
-     * Called when a publish or subscribe discovery session cannot be created:
-     * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
-     * android.os.Handler)} or
-     * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
-     * android.os.Handler)}, or when a configuration update fails:
-     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
-     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
-     * <p>
-     *     For discovery session updates failure leaves the session running with its previous
-     *     configuration - the discovery session is not terminated.
-     */
-    public void onSessionConfigFailed() {
-        /* empty */
-    }
-
-    /**
-     * Called when a discovery session (publish or subscribe) terminates. Termination may be due
-     * to user-request (either directly through {@link WifiAwareDiscoveryBaseSession#destroy()} or
-     * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
-     * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure.
-     *
-     * @param reason The termination reason using
-     *            {@code WifiAwareDiscoverySessionCallback.TERMINATE_*} codes.
-     */
-    public void onSessionTerminated(@SessionTerminateCodes int reason) {
-        /* empty */
-    }
-
-    /**
-     * Called when a discovery (publish or subscribe) operation results in a
-     * service discovery.
-     *
-     * @param peerHandle An opaque handle to the peer matching our discovery operation.
-     * @param serviceSpecificInfo The service specific information (arbitrary
-     *            byte array) provided by the peer as part of its discovery
-     *            configuration.
-     * @param matchFilter The filter which resulted in this service discovery.
-     */
-    public void onServiceDiscovered(WifiAwareManager.PeerHandle peerHandle,
-            byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
-        /* empty */
-    }
-
-    /**
-     * Called in response to
-     * {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int, byte[])}
-     * when a message is transmitted successfully - i.e. when it was received successfully by the
-     * peer (corresponds to an ACK being received).
-     * <p>
-     * Note that either this callback or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)} will be
-     * received - never both.
-     *
-     * @param messageId The arbitrary message ID specified when sending the message.
-     */
-    public void onMessageSendSucceeded(@SuppressWarnings("unused") int messageId) {
-        /* empty */
-    }
-
-    /**
-     * Called when message transmission fails - when no ACK is received from the peer.
-     * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
-     * the {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int,
-     * byte[], int)} method) - this event is received after all retries are exhausted.
-     * <p>
-     * Note that either this callback or
-     * {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)} will be received
-     * - never both.
-     *
-     * @param messageId The arbitrary message ID specified when sending the message.
-     */
-    public void onMessageSendFailed(@SuppressWarnings("unused") int messageId) {
-        /* empty */
-    }
-
-    /**
-     * Called when a message is received from a discovery session peer - in response to the
-     * peer's {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int,
-     * byte[])} or {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle,
-     * int, byte[], int)}.
-     *
-     * @param peerHandle An opaque handle to the peer matching our discovery operation.
-     * @param message A byte array containing the message.
-     */
-    public void onMessageReceived(WifiAwareManager.PeerHandle peerHandle, byte[] message) {
-        /* empty */
-    }
-}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 029794d..8c0a3a0 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -58,14 +58,14 @@
  * The class provides access to:
  * <ul>
  * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to
- * {@link #attach(WifiAwareAttachCallback, Handler)}.
+ * {@link #attach(AttachCallback, Handler)}.
  * <li>Create discovery sessions (publish or subscribe sessions). Refer to
- * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, Handler)} and
- * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback, Handler)}.
+ * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} and
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}.
  * <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(PeerHandle, byte[])} and
+ * {@link DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])} and
  * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
  * </ul>
  * <p>
@@ -75,37 +75,37 @@
  *     broadcast. Note that this broadcast is not sticky - you should register for it and then
  *     check the above API to avoid a race condition.
  * <p>
- *     An application must use {@link #attach(WifiAwareAttachCallback, Handler)} to initialize a
+ *     An application must use {@link #attach(AttachCallback, Handler)} to initialize a
  *     Aware cluster - before making any other Aware operation. Aware cluster membership is a
  *     device-wide operation - the API guarantees that the device is in a cluster or joins a
  *     Aware cluster (or starts one if none can be found). Information about attach success (or
- *     failure) are returned in callbacks of {@link WifiAwareAttachCallback}. Proceed with Aware
+ *     failure) are returned in callbacks of {@link AttachCallback}. Proceed with Aware
  *     discovery or connection setup only after receiving confirmation that Aware attach
- *     succeeded - {@link WifiAwareAttachCallback#onAttached(WifiAwareSession)}. When an
+ *     succeeded - {@link AttachCallback#onAttached(WifiAwareSession)}. When an
  *     application is finished using Aware it <b>must</b> use the
  *     {@link WifiAwareSession#destroy()} API to indicate to the Aware service that the device
  *     may detach from the Aware cluster. The device will actually disable Aware once the last
  *     application detaches.
  * <p>
  *     Once a Aware attach is confirmed use the
- *     {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, Handler)}
+ *     {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)}
  *     or
- *     {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ *     {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
  *     Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the
- *     provided callback object {@link WifiAwareDiscoverySessionCallback}. Specifically, the
- *     {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}
+ *     provided callback object {@link DiscoverySessionCallback}. Specifically, the
+ *     {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)}
  *     and
- *     {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
- *     WifiAwareSubscribeDiscoverySession)}
- *     return {@link WifiAwarePublishDiscoverySession} and
- *     {@link WifiAwareSubscribeDiscoverySession}
+ *     {@link DiscoverySessionCallback#onSubscribeStarted(
+ *SubscribeDiscoverySession)}
+ *     return {@link PublishDiscoverySession} and
+ *     {@link SubscribeDiscoverySession}
  *     objects respectively on which additional session operations can be performed, e.g. updating
- *     the session {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} and
- *     {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can
+ *     the session {@link PublishDiscoverySession#updatePublish(PublishConfig)} and
+ *     {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can
  *     also be used to send messages using the
- *     {@link WifiAwareDiscoveryBaseSession#sendMessage(PeerHandle, int, byte[])} APIs. When an
+ *     {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} APIs. When an
  *     application is finished with a discovery session it <b>must</b> terminate it using the
- *     {@link WifiAwareDiscoveryBaseSession#destroy()} API.
+ *     {@link DiscoverySession#destroy()} API.
  * <p>
  *    Creating connections between Aware devices is managed by the standard
  *    {@link ConnectivityManager#requestNetwork(NetworkRequest,
@@ -116,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(PeerHandle, byte[])}.
+ *        {@link DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])}.
  *    </ul>
  *
  * @hide PROPOSED_AWARE_API
@@ -226,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(PeerHandle, byte[])
+     * @see DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])
      * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
@@ -235,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(PeerHandle, byte[])
+     * @see DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])
      * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
@@ -307,7 +307,7 @@
      * @return An object specifying configuration limitations of Aware.
      * @hide PROPOSED_AWARE_API
      */
-    public WifiAwareCharacteristics getCharacteristics() {
+    public Characteristics getCharacteristics() {
         try {
             return mService.getCharacteristics();
         } catch (RemoteException e) {
@@ -328,12 +328,12 @@
      * attachCallback}.
      *
      * @param attachCallback A callback for attach events, extended from
-     * {@link WifiAwareAttachCallback}.
+     * {@link AttachCallback}.
      * @param handler The Handler on whose thread to execute the callbacks of the {@code
      * attachCallback} object. If a null is provided then the application's main thread will be
      *                used.
      */
-    public void attach(@NonNull WifiAwareAttachCallback attachCallback, @Nullable Handler handler) {
+    public void attach(@NonNull AttachCallback attachCallback, @Nullable Handler handler) {
         attach(handler, null, attachCallback, null);
     }
 
@@ -353,28 +353,28 @@
      * on startup and whenever it is updated (it is randomized at regular intervals for privacy).
      * The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
      * permission to execute this attach request. Otherwise, use the
-     * {@link #attach(WifiAwareAttachCallback, Handler)} version. Note that aside from permission
+     * {@link #attach(AttachCallback, Handler)} version. Note that aside from permission
      * requirements this listener will wake up the host at regular intervals causing higher power
      * consumption, do not use it unless the information is necessary (e.g. for OOB discovery).
      *
      * @param attachCallback A callback for attach events, extended from
-     * {@link WifiAwareAttachCallback}.
+     * {@link AttachCallback}.
      * @param identityChangedListener A listener for changed identity, extended from
-     * {@link WifiAwareIdentityChangedListener}.
+     * {@link IdentityChangedListener}.
      * @param handler The Handler on whose thread to execute the callbacks of the {@code
      * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the
      *                application's main thread will be used.
      */
-    public void attach(@NonNull WifiAwareAttachCallback attachCallback,
-            @NonNull WifiAwareIdentityChangedListener identityChangedListener,
+    public void attach(@NonNull AttachCallback attachCallback,
+            @NonNull IdentityChangedListener identityChangedListener,
             @Nullable Handler handler) {
         attach(handler, null, attachCallback, identityChangedListener);
     }
 
     /** @hide */
     public void attach(Handler handler, ConfigRequest configRequest,
-            WifiAwareAttachCallback attachCallback,
-            WifiAwareIdentityChangedListener identityChangedListener) {
+            AttachCallback attachCallback,
+            IdentityChangedListener identityChangedListener) {
         if (VDBG) {
             Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback
                     + ", configRequest=" + configRequest + ", identityChangedListener="
@@ -409,7 +409,7 @@
 
     /** @hide */
     public void publish(int clientId, Looper looper, PublishConfig publishConfig,
-            WifiAwareDiscoverySessionCallback callback) {
+            DiscoverySessionCallback callback) {
         if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig);
 
         try {
@@ -437,7 +437,7 @@
 
     /** @hide */
     public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig,
-            WifiAwareDiscoverySessionCallback callback) {
+            DiscoverySessionCallback callback) {
         if (VDBG) {
             if (VDBG) {
                 Log.v(TAG,
@@ -672,14 +672,14 @@
         }
 
         /**
-         * Constructs a {@link WifiAwareAttachCallback} using the specified looper.
+         * Constructs a {@link AttachCallback} using the specified looper.
          * All callbacks will delivered on the thread of the specified looper.
          *
          * @param looper The looper on which to execute the callbacks.
          */
         WifiAwareEventCallbackProxy(WifiAwareManager mgr, Looper looper, Binder binder,
-                final WifiAwareAttachCallback attachCallback,
-                final WifiAwareIdentityChangedListener identityChangedListener) {
+                final AttachCallback attachCallback,
+                final IdentityChangedListener identityChangedListener) {
             mAwareManager = new WeakReference<>(mgr);
             mLooper = looper;
             mBinder = binder;
@@ -828,14 +828,14 @@
 
         private final WeakReference<WifiAwareManager> mAwareManager;
         private final boolean mIsPublish;
-        private final WifiAwareDiscoverySessionCallback mOriginalCallback;
+        private final DiscoverySessionCallback mOriginalCallback;
         private final int mClientId;
 
         private final Handler mHandler;
-        private WifiAwareDiscoveryBaseSession mSession;
+        private DiscoverySession mSession;
 
         WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper,
-                boolean isPublish, WifiAwareDiscoverySessionCallback originalCallback,
+                boolean isPublish, DiscoverySessionCallback originalCallback,
                 int clientId) {
             mAwareManager = new WeakReference<>(mgr);
             mIsPublish = isPublish;
@@ -1006,13 +1006,13 @@
             }
 
             if (mIsPublish) {
-                WifiAwarePublishDiscoverySession session = new WifiAwarePublishDiscoverySession(mgr,
+                PublishDiscoverySession session = new PublishDiscoverySession(mgr,
                         mClientId, sessionId);
                 mSession = session;
                 mOriginalCallback.onPublishStarted(session);
             } else {
-                WifiAwareSubscribeDiscoverySession
-                        session = new WifiAwareSubscribeDiscoverySession(mgr, mClientId, sessionId);
+                SubscribeDiscoverySession
+                        session = new SubscribeDiscoverySession(mgr, mClientId, sessionId);
                 mSession = session;
                 mOriginalCallback.onSubscribeStarted(session);
             }
@@ -1027,18 +1027,7 @@
                 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
             }
             mAwareManager.clear();
-            mOriginalCallback.onSessionTerminated(reason);
+            mOriginalCallback.onSessionTerminated();
         }
     }
-
-    /** @hide PROPOSED_AWARE_API */
-    public static class PeerHandle {
-        /** @hide */
-        public PeerHandle(int peerId) {
-            this.peerId = peerId;
-        }
-
-        /** @hide */
-        public int peerId;
-    }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 005895a..e3ebe86 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -65,7 +65,7 @@
      * session-wide destroy.
      * <p>
      * An application may re-attach after a destroy using
-     * {@link WifiAwareManager#attach(WifiAwareAttachCallback, Handler)} .
+     * {@link WifiAwareManager#attach(AttachCallback, Handler)} .
      */
     public void destroy() {
         WifiAwareManager mgr = mMgr.get();
@@ -95,22 +95,22 @@
     /**
      * Issue a request to the Aware service to create a new Aware publish discovery session, using
      * the specified {@code publishConfig} configuration. The results of the publish operation
-     * are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
+     * are routed to the callbacks of {@link DiscoverySessionCallback}:
      * <ul>
      *     <li>
-     *     {@link WifiAwareDiscoverySessionCallback#onPublishStarted(
-     *     WifiAwarePublishDiscoverySession)}
+     *     {@link DiscoverySessionCallback#onPublishStarted(
+     *PublishDiscoverySession)}
      *     is called when the publish session is created and provides a handle to the session.
      *     Further operations on the publish session can be executed on that object.
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigFailed()} is called if the
      *     publish operation failed.
      * </ul>
      * <p>
      * Other results of the publish session operations will also be routed to callbacks
      * on the {@code callback} object. The resulting publish session can be modified using
-     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
+     * {@link PublishDiscoverySession#updatePublish(PublishConfig)}.
      * <p>
-     *      An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
+     *      An application must use the {@link DiscoverySession#destroy()} to
      *      terminate the publish discovery session once it isn't needed. This will free
      *      resources as well terminate any on-air transmissions.
      * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
@@ -118,13 +118,13 @@
      *
      * @param publishConfig The {@link PublishConfig} specifying the
      *            configuration of the requested publish session.
-     * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
+     * @param callback A {@link DiscoverySessionCallback} derived object to be used for
      *                 session event callbacks.
      * @param handler The Handler on whose thread to execute the callbacks of the {@code
      * callback} object. If a null is provided then the application's main thread will be used.
      */
     public void publish(@NonNull PublishConfig publishConfig,
-            @NonNull WifiAwareDiscoverySessionCallback callback, @Nullable Handler handler) {
+            @NonNull DiscoverySessionCallback callback, @Nullable Handler handler) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "publish: called post GC on WifiAwareManager");
@@ -141,22 +141,22 @@
     /**
      * Issue a request to the Aware service to create a new Aware subscribe discovery session, using
      * the specified {@code subscribeConfig} configuration. The results of the subscribe
-     * operation are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
+     * operation are routed to the callbacks of {@link DiscoverySessionCallback}:
      * <ul>
      *     <li>
-     *  {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
-     *  WifiAwareSubscribeDiscoverySession)}
+     *  {@link DiscoverySessionCallback#onSubscribeStarted(
+     *SubscribeDiscoverySession)}
      *     is called when the subscribe session is created and provides a handle to the session.
      *     Further operations on the subscribe session can be executed on that object.
-     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+     *     <li>{@link DiscoverySessionCallback#onSessionConfigFailed()} is called if the
      *     subscribe operation failed.
      * </ul>
      * <p>
      * Other results of the subscribe session operations will also be routed to callbacks
      * on the {@code callback} object. The resulting subscribe session can be modified using
-     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
      * <p>
-     *      An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
+     *      An application must use the {@link DiscoverySession#destroy()} to
      *      terminate the subscribe discovery session once it isn't needed. This will free
      *      resources as well terminate any on-air transmissions.
      * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
@@ -164,13 +164,13 @@
      *
      * @param subscribeConfig The {@link SubscribeConfig} specifying the
      *            configuration of the requested subscribe session.
-     * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
+     * @param callback A {@link DiscoverySessionCallback} derived object to be used for
      *                 session event callbacks.
      * @param handler The Handler on whose thread to execute the callbacks of the {@code
      * callback} object. If a null is provided then the application's main thread will be used.
      */
     public void subscribe(@NonNull SubscribeConfig subscribeConfig,
-            @NonNull WifiAwareDiscoverySessionCallback callback, @Nullable Handler handler) {
+            @NonNull DiscoverySessionCallback callback, @Nullable Handler handler) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "publish: called post GC on WifiAwareManager");
@@ -193,7 +193,7 @@
      *     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(WifiAwareManager.PeerHandle,
+     *     {@link DiscoverySession#createNetworkSpecifier(PeerHandle,
      *     byte[])}.
      *
      * @param role  The role of this device:
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 12d4995..18f6bc8 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -71,8 +71,7 @@
     private Handler mHandler;
     private List<ScoredNetwork> mUpdatedNetworksCaptor;
     private ScoredNetwork mValidScoredNetwork;
-    private WifiNetworkScoreCache mScoreCache =
-                new WifiNetworkScoreCache(mockContext);
+    private WifiNetworkScoreCache mScoreCache;
 
     private static ScanResult buildScanResult(String ssid, String bssid) {
         return new ScanResult(
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 24c0127..a396d87 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -67,19 +67,19 @@
     public Context mockContext;
 
     @Mock
-    public WifiAwareAttachCallback mockCallback;
+    public AttachCallback mockCallback;
 
     @Mock
-    public WifiAwareDiscoverySessionCallback mockSessionCallback;
+    public DiscoverySessionCallback mockSessionCallback;
 
     @Mock
     public IWifiAwareManager mockAwareService;
 
     @Mock
-    public WifiAwarePublishDiscoverySession mockPublishSession;
+    public PublishDiscoverySession mockPublishSession;
 
     @Mock
-    public WifiAwareSubscribeDiscoverySession mockSubscribeSession;
+    public SubscribeDiscoverySession mockSubscribeSession;
 
     @Mock
     public RttManager.RttListener mockRttListener;
@@ -276,7 +276,7 @@
         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 PeerHandle peerHandle = new PeerHandle(873);
         final String string1 = "hey from here...";
         final byte[] matchFilter = { 1, 12, 2, 31, 32 };
         final int messageId = 2123;
@@ -290,10 +290,9 @@
                 .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<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
+        ArgumentCaptor<PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(PeerHandle.class);
         ArgumentCaptor<List<byte[]>> matchFilterCaptor = ArgumentCaptor.forClass(
                 (Class) List.class);
 
@@ -377,7 +376,6 @@
         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);
@@ -387,8 +385,8 @@
                 .forClass(IWifiAwareEventCallback.class);
         ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
                 .forClass(IWifiAwareDiscoverySessionCallback.class);
-        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
-                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
 
         // (1) connect successfully
         mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
@@ -404,10 +402,10 @@
         inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
                 sessionProxyCallback.capture());
         sessionProxyCallback.getValue().onSessionStarted(sessionId);
-        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        sessionProxyCallback.getValue().onSessionTerminated(0);
         mMockLooper.dispatchAll();
         inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
-        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+        inOrder.verify(mockSessionCallback).onSessionTerminated();
 
         // (3) failure when trying to update: NOP
         publishSession.getValue().updatePublish(publishConfig);
@@ -428,7 +426,7 @@
         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 PeerHandle peerHandle = new PeerHandle(873);
         final String string1 = "hey from here...";
         final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
         final int messageId = 2123;
@@ -442,10 +440,9 @@
                 .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);
+        ArgumentCaptor<SubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(SubscribeDiscoverySession.class);
+        ArgumentCaptor<PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(PeerHandle.class);
 
         // (0) connect + success
         mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
@@ -516,7 +513,6 @@
         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);
@@ -526,8 +522,8 @@
                 .forClass(IWifiAwareEventCallback.class);
         ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
                 .forClass(IWifiAwareDiscoverySessionCallback.class);
-        ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
-                .forClass(WifiAwareSubscribeDiscoverySession.class);
+        ArgumentCaptor<SubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(SubscribeDiscoverySession.class);
 
         // (1) connect successfully
         mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
@@ -543,10 +539,10 @@
         inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
                 sessionProxyCallback.capture());
         sessionProxyCallback.getValue().onSessionStarted(sessionId);
-        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        sessionProxyCallback.getValue().onSessionTerminated(0);
         mMockLooper.dispatchAll();
         inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
-        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+        inOrder.verify(mockSessionCallback).onSessionTerminated();
 
         // (3) failure when trying to update: NOP
         subscribeSession.getValue().updateSubscribe(subscribeConfig);
@@ -892,8 +888,8 @@
                 .forClass(IWifiAwareEventCallback.class);
         ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
                 .forClass(IWifiAwareDiscoverySessionCallback.class);
-        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
-                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
         ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
                 .forClass(RttManager.ParcelableRttParams.class);
         ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
@@ -953,7 +949,7 @@
     public void testNetworkSpecifierWithClient() throws Exception {
         final int clientId = 4565;
         final int sessionId = 123;
-        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(123412);
+        final PeerHandle peerHandle = new 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();
@@ -967,8 +963,8 @@
                 .forClass(IWifiAwareEventCallback.class);
         ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
                 .forClass(IWifiAwareDiscoverySessionCallback.class);
-        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
-                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
 
         InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);