Merge "Get PackageManager on demand in ActivityStarter."
diff --git a/Android.bp b/Android.bp
index a740e22..2ea4894 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
             // runtime, as well as the only protos that are actually
             // needed by the device.
             srcs: [
+                "core/proto/android/os/cpufreq.proto",
                 "core/proto/android/os/cpuinfo.proto",
                 "core/proto/android/os/kernelwake.proto",
                 "core/proto/android/os/pagetypeinfo.proto",
@@ -83,6 +84,7 @@
     ],
 
     srcs: [
+        "core/proto/android/os/cpufreq.proto",
         "core/proto/android/os/cpuinfo.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
diff --git a/Android.mk b/Android.mk
index 24e717d..08a8b64 100644
--- a/Android.mk
+++ b/Android.mk
@@ -273,7 +273,6 @@
 	core/java/android/os/IRecoverySystemProgressListener.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
-	core/java/android/os/IStatsCallbacks.aidl \
 	core/java/android/os/IStatsCompanionService.aidl \
 	core/java/android/os/IStatsManager.aidl \
 	core/java/android/os/IThermalEventListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index b1ca3e8..dca7528 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6973,11 +6973,12 @@
 
   public final class Slice implements android.os.Parcelable {
     ctor protected Slice(android.os.Parcel);
-    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
-    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getHints();
     method public java.util.List<android.app.slice.SliceItem> getItems();
+    method public android.app.slice.SliceSpec getSpec();
     method public android.net.Uri getUri();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -6986,55 +6987,59 @@
     field public static final java.lang.String HINT_LARGE = "large";
     field public static final java.lang.String HINT_LIST = "list";
     field public static final java.lang.String HINT_LIST_ITEM = "list_item";
-    field public static final java.lang.String HINT_MESSAGE = "message";
     field public static final java.lang.String HINT_NO_TINT = "no_tint";
     field public static final java.lang.String HINT_PARTIAL = "partial";
     field public static final java.lang.String HINT_SELECTED = "selected";
-    field public static final java.lang.String HINT_SOURCE = "source";
     field public static final java.lang.String HINT_TITLE = "title";
+    field public static final java.lang.String SUBTYPE_MESSAGE = "message";
+    field public static final java.lang.String SUBTYPE_SOURCE = "source";
   }
 
   public static class Slice.Builder {
     ctor public Slice.Builder(android.net.Uri);
     ctor public Slice.Builder(android.app.slice.Slice.Builder);
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addColor(int, java.lang.String...);
-    method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice build();
+    method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
   }
 
   public final class SliceItem implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.PendingIntent getAction();
     method public int getColor();
+    method public java.lang.String getFormat();
     method public java.util.List<java.lang.String> getHints();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput getRemoteInput();
     method public android.app.slice.Slice getSlice();
+    method public java.lang.String getSubType();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
-    method public int getType();
     method public boolean hasHint(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR;
-    field public static final int TYPE_ACTION = 4; // 0x4
-    field public static final int TYPE_COLOR = 6; // 0x6
-    field public static final int TYPE_IMAGE = 3; // 0x3
-    field public static final int TYPE_REMOTE_INPUT = 9; // 0x9
-    field public static final int TYPE_SLICE = 1; // 0x1
-    field public static final int TYPE_TEXT = 2; // 0x2
-    field public static final int TYPE_TIMESTAMP = 8; // 0x8
+    field public static final java.lang.String FORMAT_ACTION = "action";
+    field public static final java.lang.String FORMAT_COLOR = "color";
+    field public static final java.lang.String FORMAT_IMAGE = "image";
+    field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
+    field public static final java.lang.String FORMAT_SLICE = "slice";
+    field public static final java.lang.String FORMAT_TEXT = "text";
+    field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
@@ -7042,7 +7047,8 @@
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
-    method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+    method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7051,25 +7057,14 @@
     field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
   }
 
-}
-
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+  public final class SliceSpec implements android.os.Parcelable {
+    ctor public SliceSpec(java.lang.String, int);
+    method public boolean canRender(android.app.slice.SliceSpec);
+    method public int describeContents();
+    method public int getRevision();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 592bd3d..aa3a1e2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7417,11 +7417,12 @@
 
   public final class Slice implements android.os.Parcelable {
     ctor protected Slice(android.os.Parcel);
-    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
-    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getHints();
     method public java.util.List<android.app.slice.SliceItem> getItems();
+    method public android.app.slice.SliceSpec getSpec();
     method public android.net.Uri getUri();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7430,55 +7431,59 @@
     field public static final java.lang.String HINT_LARGE = "large";
     field public static final java.lang.String HINT_LIST = "list";
     field public static final java.lang.String HINT_LIST_ITEM = "list_item";
-    field public static final java.lang.String HINT_MESSAGE = "message";
     field public static final java.lang.String HINT_NO_TINT = "no_tint";
     field public static final java.lang.String HINT_PARTIAL = "partial";
     field public static final java.lang.String HINT_SELECTED = "selected";
-    field public static final java.lang.String HINT_SOURCE = "source";
     field public static final java.lang.String HINT_TITLE = "title";
+    field public static final java.lang.String SUBTYPE_MESSAGE = "message";
+    field public static final java.lang.String SUBTYPE_SOURCE = "source";
   }
 
   public static class Slice.Builder {
     ctor public Slice.Builder(android.net.Uri);
     ctor public Slice.Builder(android.app.slice.Slice.Builder);
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addColor(int, java.lang.String...);
-    method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice build();
+    method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
   }
 
   public final class SliceItem implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.PendingIntent getAction();
     method public int getColor();
+    method public java.lang.String getFormat();
     method public java.util.List<java.lang.String> getHints();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput getRemoteInput();
     method public android.app.slice.Slice getSlice();
+    method public java.lang.String getSubType();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
-    method public int getType();
     method public boolean hasHint(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR;
-    field public static final int TYPE_ACTION = 4; // 0x4
-    field public static final int TYPE_COLOR = 6; // 0x6
-    field public static final int TYPE_IMAGE = 3; // 0x3
-    field public static final int TYPE_REMOTE_INPUT = 9; // 0x9
-    field public static final int TYPE_SLICE = 1; // 0x1
-    field public static final int TYPE_TEXT = 2; // 0x2
-    field public static final int TYPE_TIMESTAMP = 8; // 0x8
+    field public static final java.lang.String FORMAT_ACTION = "action";
+    field public static final java.lang.String FORMAT_COLOR = "color";
+    field public static final java.lang.String FORMAT_IMAGE = "image";
+    field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
+    field public static final java.lang.String FORMAT_SLICE = "slice";
+    field public static final java.lang.String FORMAT_TEXT = "text";
+    field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
@@ -7486,7 +7491,8 @@
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
-    method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+    method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7495,25 +7501,14 @@
     field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
   }
 
-}
-
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+  public final class SliceSpec implements android.os.Parcelable {
+    ctor public SliceSpec(java.lang.String, int);
+    method public boolean canRender(android.app.slice.SliceSpec);
+    method public int describeContents();
+    method public int getRevision();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
   }
 
 }
@@ -48194,8 +48189,8 @@
   }
 
   public final class StatsManager {
-    method public byte[] getData(java.lang.String);
     method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
+    method public byte[] getData(java.lang.String);
     method public boolean removeConfiguration(java.lang.String);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 98615ee..57b5222 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7047,11 +7047,12 @@
 
   public final class Slice implements android.os.Parcelable {
     ctor protected Slice(android.os.Parcel);
-    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
-    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getHints();
     method public java.util.List<android.app.slice.SliceItem> getItems();
+    method public android.app.slice.SliceSpec getSpec();
     method public android.net.Uri getUri();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7060,55 +7061,59 @@
     field public static final java.lang.String HINT_LARGE = "large";
     field public static final java.lang.String HINT_LIST = "list";
     field public static final java.lang.String HINT_LIST_ITEM = "list_item";
-    field public static final java.lang.String HINT_MESSAGE = "message";
     field public static final java.lang.String HINT_NO_TINT = "no_tint";
     field public static final java.lang.String HINT_PARTIAL = "partial";
     field public static final java.lang.String HINT_SELECTED = "selected";
-    field public static final java.lang.String HINT_SOURCE = "source";
     field public static final java.lang.String HINT_TITLE = "title";
+    field public static final java.lang.String SUBTYPE_MESSAGE = "message";
+    field public static final java.lang.String SUBTYPE_SOURCE = "source";
   }
 
   public static class Slice.Builder {
     ctor public Slice.Builder(android.net.Uri);
     ctor public Slice.Builder(android.app.slice.Slice.Builder);
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addColor(int, java.lang.String...);
-    method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
+    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice build();
+    method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
   }
 
   public final class SliceItem implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.PendingIntent getAction();
     method public int getColor();
+    method public java.lang.String getFormat();
     method public java.util.List<java.lang.String> getHints();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput getRemoteInput();
     method public android.app.slice.Slice getSlice();
+    method public java.lang.String getSubType();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
-    method public int getType();
     method public boolean hasHint(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR;
-    field public static final int TYPE_ACTION = 4; // 0x4
-    field public static final int TYPE_COLOR = 6; // 0x6
-    field public static final int TYPE_IMAGE = 3; // 0x3
-    field public static final int TYPE_REMOTE_INPUT = 9; // 0x9
-    field public static final int TYPE_SLICE = 1; // 0x1
-    field public static final int TYPE_TEXT = 2; // 0x2
-    field public static final int TYPE_TIMESTAMP = 8; // 0x8
+    field public static final java.lang.String FORMAT_ACTION = "action";
+    field public static final java.lang.String FORMAT_COLOR = "color";
+    field public static final java.lang.String FORMAT_IMAGE = "image";
+    field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
+    field public static final java.lang.String FORMAT_SLICE = "slice";
+    field public static final java.lang.String FORMAT_TEXT = "text";
+    field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
@@ -7116,7 +7121,8 @@
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
-    method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+    method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7125,25 +7131,14 @@
     field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
   }
 
-}
-
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+  public final class SliceSpec implements android.os.Parcelable {
+    ctor public SliceSpec(java.lang.String, int);
+    method public boolean canRender(android.app.slice.SliceSpec);
+    method public int describeContents();
+    method public int getRevision();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
   }
 
 }
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 8239d8e..c8a0883 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "incident_helper"
 
+#include "parsers/CpuFreqParser.h"
 #include "parsers/CpuInfoParser.h"
 #include "parsers/KernelWakesParser.h"
 #include "parsers/PageTypeInfoParser.h"
@@ -60,6 +61,8 @@
             return new KernelWakesParser();
         case 2003:
             return new CpuInfoParser();
+        case 2004:
+            return new CpuFreqParser();
         default:
             return NULL;
     }
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
new file mode 100644
index 0000000..02f1ce7
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+#include <unistd.h>
+
+#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
+#include "ih_util.h"
+#include "CpuFreqParser.h"
+
+using namespace android::os;
+
+status_t
+CpuFreqParser::Parse(const int in, const int out) const
+{
+    Reader reader(in);
+    string line;
+
+    // parse header
+    reader.readLine(&line);
+    header_t header = parseHeader(line, TAB_DELIMITER);
+    if (header.size() < 1) {
+        fprintf(stderr, "Bad header: %s\n", line.c_str());
+        return BAD_VALUE;
+    }
+    const int numCpus = (int)header.size() - 1;
+    vector<pair<int, long long>> cpucores[numCpus];
+
+    // parse freq and time
+    while (reader.readLine(&line)) {
+        if (line.empty()) continue;
+
+        record_t record = parseRecord(line, TAB_DELIMITER);
+        if (record.size() != header.size()) {
+            fprintf(stderr, "Bad line: %s\n", line.c_str());
+            continue;
+        }
+
+        int freq = toInt(record[0]);
+        for (int i=0; i<numCpus; i++) {
+            if (strcmp(record[i+1].c_str(), "N/A") == 0) {
+                continue;
+            }
+            cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
+        }
+    }
+
+    ProtoOutputStream proto;
+
+    long jiffyHz = sysconf(_SC_CLK_TCK);
+    proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+
+    for (int i=0; i<numCpus; i++) {
+        long long token = proto.start(CpuFreq::CPU_FREQS);
+        proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+        for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
+            long long stateToken = proto.start(CpuFreqStats::TIMES);
+            proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
+            proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+            proto.end(stateToken);
+        }
+        proto.end(token);
+    }
+
+    if (!reader.ok(&line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
+
+    if (!proto.flush(out)) {
+        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+        return -1;
+    }
+    fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+    return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.h b/cmds/incident_helper/src/parsers/CpuFreqParser.h
new file mode 100644
index 0000000..470d568
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef CPU_FREQ_PARSER_H
+#define CPU_FREQ_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
+ */
+class CpuFreqParser : public TextParserBase {
+public:
+    CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
+    ~CpuFreqParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif  // CPU_FREQ_PARSER_H
diff --git a/cmds/incident_helper/testdata/cpufreq.txt b/cmds/incident_helper/testdata/cpufreq.txt
new file mode 100644
index 0000000..6472839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpufreq.txt
@@ -0,0 +1,6 @@
+freq		cpu0		cpu1		cpu2		cpu3		
+307200		23860761		23860761		23890935		23890935		
+384000		83124		83124		29383		29383		
+748800		N/A		N/A		10547		10547		
+768000		22652		22652		N/A		N/A		
+825600		N/A		N/A		13173		13173			
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
new file mode 100644
index 0000000..1c2f9e5
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#include "CpuFreqParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpufreq.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuFreqParserTest : public Test {
+public:
+    virtual void SetUp() override {
+        ASSERT_TRUE(tf.fd != -1);
+    }
+
+    string getSerializedString(::google::protobuf::Message& message) {
+        string expectedStr;
+        message.SerializeToFileDescriptor(tf.fd);
+        ReadFileToString(tf.path, &expectedStr);
+        return expectedStr;
+    }
+
+protected:
+    TemporaryFile tf;
+
+    const string kTestPath = GetExecutableDirectory();
+    const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuFreqParserTest, Success) {
+    const string testFile = kTestDataPath + "cpufreq.txt";
+    CpuFreqParser parser;
+    CpuFreq expected;
+
+    long jiffyHz = sysconf(_SC_CLK_TCK);
+    expected.set_jiffy_hz(jiffyHz);
+
+    CpuFreqStats::TimeInState* state;
+
+    CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+    cpu0->set_cpu_name("cpu0");
+    state = cpu0->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23860761);
+    state = cpu0->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(83124);
+    state = cpu0->add_times();
+    state->set_state_khz(768000);
+    state->set_time_jiffy(22652);
+
+    CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+    cpu1->set_cpu_name("cpu1");
+    state = cpu1->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23860761);
+    state = cpu1->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(83124);
+    state = cpu1->add_times();
+    state->set_state_khz(768000);
+    state->set_time_jiffy(22652);
+
+    CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+    cpu2->set_cpu_name("cpu2");
+    state = cpu2->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23890935);
+    state = cpu2->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(29383);
+    state = cpu2->add_times();
+    state->set_state_khz(748800);
+    state->set_time_jiffy(10547);
+    state = cpu2->add_times();
+    state->set_state_khz(825600);
+    state->set_time_jiffy(13173);
+
+    CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+    cpu3->set_cpu_name("cpu3");
+    state = cpu3->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23890935);
+    state = cpu3->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(29383);
+    state = cpu3->add_times();
+    state->set_state_khz(748800);
+    state->set_time_jiffy(10547);
+    state = cpu3->add_times();
+    state->set_state_khz(825600);
+    state->set_time_jiffy(13173);
+
+    int fd = open(testFile.c_str(), O_RDONLY);
+    ASSERT_TRUE(fd != -1);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+    EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+    close(fd);
+}
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index 57ad15c..bbc14bc 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -56,7 +56,7 @@
     const string kTestDataPath = kTestPath + "/testdata/";
 };
 
-TEST_F(CpuInfoParserTest, HasSwapInfo) {
+TEST_F(CpuInfoParserTest, Success) {
     const string testFile = kTestDataPath + "cpuinfo.txt";
     CpuInfoParser parser;
     CpuInfo expected;
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index b7633a4..30dd339 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <wait.h>
 
+const bool DEBUG = false;
 const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
 const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
 
@@ -71,9 +72,11 @@
             mTimedOut = true;
             break;
         } else if (count < 0) {
+            if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
             return -errno;
         } else {
             if ((pfds.revents & POLLERR) != 0) {
+                if (DEBUG) ALOGD("return event has error %s", strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             } else {
                 ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
@@ -81,6 +84,7 @@
                     if (errno == EAGAIN || errno == EWOULDBLOCK) {
                         continue;
                     } else {
+                        if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
                         return -errno;
                     }
                 } else if (amt == 0) {
@@ -95,7 +99,7 @@
 }
 
 status_t
-FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
+FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
 {
     struct pollfd pfds[] = {
         { .fd = fd,     .events = POLLIN  },
@@ -135,12 +139,18 @@
             mTimedOut = true;
             break;
         } else if (count < 0) {
+            if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
             return -errno;
         }
 
         // make sure no errors occur on any fds
         for (int i = 0; i < 3; ++i) {
             if ((pfds[i].revents & POLLERR) != 0) {
+                if (i == 0 && isSysfs) {
+                    if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
+                    continue;
+                }
+                if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             }
         }
@@ -155,6 +165,7 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
                 } // otherwise just continue
             } else if (amt == 0) {  // reach EOF so don't have to poll pfds[0].
@@ -176,6 +187,7 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
                     return -errno;
                 } // otherwise just continue
             } else {
@@ -202,6 +214,7 @@
         ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
         if (amt < 0) {
             if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
                 return -errno;
             } // otherwise just continue
         } else if (amt == 0) {
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 8857ae7..48dc855 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -47,8 +47,10 @@
      * and stores the processed data from 'fromFd' in memory for later usage.
      * This function behaves in a streaming fashion in order to save memory usage.
      * Returns NO_ERROR if there were no errors or if we timed out.
+     *
+     * Poll will return POLLERR if fd is from sysfs, handle this edge case.
      */
-    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
+    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
 
     /**
      * Whether we timed out.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 892bcca..c08b9ea 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -232,6 +232,7 @@
      mFilename(filename)
 {
     name = filename;
+    mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
 }
 
 FileSection::~FileSection() {}
@@ -264,7 +265,7 @@
 
     // parent process
     status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
-            this->timeoutMs);
+            this->timeoutMs, mIsSysfs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
             this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0a1e03e..64558a6 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -69,6 +69,7 @@
 
 private:
     const char* mFilename;
+    bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
 };
 
 /**
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d860363..1b2f9da 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,13 +15,13 @@
 LOCAL_PATH:= $(call my-dir)
 
 statsd_common_src := \
-    ../../core/java/android/os/IStatsCallbacks.aidl \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/stats_log.proto \
     src/statsd_config.proto \
     src/atoms_copy.proto \
     src/anomaly/AnomalyMonitor.cpp \
+    src/anomaly/AnomalyTracker.cpp \
     src/condition/CombinationConditionTracker.cpp \
     src/condition/condition_util.cpp \
     src/condition/SimpleConditionTracker.cpp \
@@ -40,7 +40,6 @@
     src/matchers/CombinationLogMatchingTracker.cpp \
     src/matchers/matcher_util.cpp \
     src/matchers/SimpleLogMatchingTracker.cpp \
-    src/anomaly/DiscreteAnomalyTracker.cpp \
     src/metrics/MetricProducer.cpp \
     src/metrics/EventMetricProducer.cpp \
     src/metrics/CountMetricProducer.cpp \
@@ -179,4 +178,3 @@
 statsd_common_c_includes:=
 
 include $(BUILD_NATIVE_TEST)
-
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index abd2a35..fcb5107 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -50,13 +50,24 @@
 const int FIELD_ID_NAME = 2;
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+                                     const sp<AnomalyMonitor>& anomalyMonitor,
                                      const std::function<void(const ConfigKey&)>& sendBroadcast)
-    : mUidMap(uidMap), mSendBroadcast(sendBroadcast) {
+    : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast) {
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
 }
 
+void StatsLogProcessor::onAnomalyAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
+    for (const auto& anomaly : anomalySet) {
+        for (const auto& itr : mMetricsManagers) {
+            itr.second->onAnomalyAlarmFired(timestampNs, anomaly);
+        }
+    }
+}
+
 // TODO: what if statsd service restarts? How do we know what logs are already processed before?
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
     // pass the event to metrics managers.
@@ -93,6 +104,7 @@
     unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
+        newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
         mMetricsManagers[key] = std::move(newMetricsManager);
         // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
         ALOGD("StatsdConfig valid");
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 2091774..510dc51 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,7 +32,7 @@
 
 class StatsLogProcessor : public ConfigListener {
 public:
-    StatsLogProcessor(const sp<UidMap>& uidMap,
+    StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
                       const std::function<void(const ConfigKey&)>& sendBroadcast);
     virtual ~StatsLogProcessor();
 
@@ -42,8 +42,11 @@
     void OnConfigRemoved(const ConfigKey& key);
 
     size_t GetMetricsSize(const ConfigKey& key);
-
+ 
     void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
+    void onAnomalyAlarmFired(
+            const uint64_t timestampNs,
+            unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet);
 
 private:
     mutable mutex mBroadcastTimesMutex;
@@ -54,6 +57,8 @@
 
     sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
 
+    sp<AnomalyMonitor> mAnomalyMonitor;
+
     /* Max *serialized* size of the logs kept in memory before flushing through binder call.
        Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
        So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ef01ec7..1a056df 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -73,7 +73,7 @@
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, [this](const ConfigKey& key) {
+    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) {
         auto sc = getStatsCompanionService();
         auto receiver = mConfigManager->GetConfigReceiver(key);
         if (sc == nullptr) {
@@ -554,7 +554,10 @@
 
     if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
     // TODO: check through all counters/timers and see if an anomaly has indeed occurred.
-
+    uint64_t currentTimeNs = time(nullptr) * NS_PER_SEC;
+    std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
+            mAnomalyMonitor->onAlarmFired(currentTimeNs);
+    mProcessor->onAnomalyAlarmFired(currentTimeNs, anomalySet);
     return Status::ok();
 }
 
@@ -690,12 +693,6 @@
 }
 
 void StatsService::binderDied(const wp <IBinder>& who) {
-    for (size_t i = 0; i < mCallbacks.size(); i++) {
-        if (IInterface::asBinder(mCallbacks[i]) == who) {
-            mCallbacks.removeAt(i);
-            break;
-        }
-    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 888f97b..4d768f6 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,7 +24,6 @@
 #include "packages/UidMap.h"
 
 #include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCallbacks.h>
 #include <android/os/IStatsCompanionService.h>
 #include <binder/IResultReceiver.h>
 #include <utils/Looper.h>
@@ -195,16 +194,6 @@
      * Whether this is an eng build.
      */
     bool mEngBuild;
-
-    /**
-     * Lock for callback handling.
-     */
-    std::mutex mLock;
-
-    /**
-     * Vector maintaining the list of callbacks for clients.
-     */
-    Vector< sp<IStatsCallbacks> > mCallbacks;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 7a46410..da52a9d 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -129,6 +129,11 @@
     return ((int64_t)timeSec) * 1000;
 }
 
+unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::onAlarmFired(
+        uint64_t timestampNs) {
+    return popSoonerThan(static_cast<uint32_t>(timestampNs));
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index d9207e9..0bd5055 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANOMALY_MONITOR_H
-#define ANOMALY_MONITOR_H
+#pragma once
 
 #include "anomaly/indexed_priority_queue.h"
 
@@ -23,6 +22,8 @@
 #include <utils/RefBase.h>
 
 #include <queue>
+#include <set>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
@@ -114,6 +115,8 @@
         return mRegisteredAlarmTimeSec;
     }
 
+    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> onAlarmFired(uint64_t timestampNs);
+
 private:
     std::mutex mLock;
 
@@ -154,5 +157,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
-#endif  // ANOMALY_MONITOR_H
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
new file mode 100644
index 0000000..0904a04
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include "AnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AnomalyTracker::AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs)
+    : mAlert(alert),
+      mBucketSizeNs(bucketSizeNs),
+      mNumOfPastPackets(mAlert.number_of_buckets() - 1) {
+    VLOG("AnomalyTracker() called");
+    if (mAlert.number_of_buckets() <= 0) {
+        ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+              (long long)mAlert.number_of_buckets());
+        return;
+    }
+    if (mBucketSizeNs <= 0) {
+        ALOGE("Cannot create DiscreteAnomalyTracker with bucket size %lld ",
+              (long long)mBucketSizeNs);
+        return;
+    }
+    if (!mAlert.has_trigger_if_sum_gt()) {
+        ALOGE("Cannot create DiscreteAnomalyTracker without threshold");
+        return;
+    }
+    reset(); // initialization
+}
+
+AnomalyTracker::~AnomalyTracker() {
+    VLOG("~AnomalyTracker() called");
+    stopAllAlarms();
+}
+
+void AnomalyTracker::reset() {
+    VLOG("reset() called.");
+    stopAllAlarms();
+    mPastBuckets.clear();
+    // Excludes the current bucket.
+    mPastBuckets.resize(mNumOfPastPackets);
+    mSumOverPastBuckets.clear();
+    mMostRecentBucketNum = -1;
+    mLastAlarmTimestampNs = -1;
+}
+
+size_t AnomalyTracker::index(int64_t bucketNum) const {
+    return bucketNum % mNumOfPastPackets;
+}
+
+void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) {
+    VLOG("addPastBucket() called.");
+    if (latestPastBucketNum <= mMostRecentBucketNum - mNumOfPastPackets) {
+        ALOGE("Cannot add a past bucket %lld units in past", (long long)latestPastBucketNum);
+        return;
+    }
+
+    // The past packets are ancient. Empty out old mPastBuckets[i] values and reset
+    // mSumOverPastBuckets.
+    if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastPackets) {
+        mPastBuckets.clear();
+        mPastBuckets.resize(mNumOfPastPackets);
+        mSumOverPastBuckets.clear();
+    } else {
+        for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastPackets + 1));
+             i <= latestPastBucketNum - mNumOfPastPackets; i++) {
+            const int idx = index(i);
+            subtractBucketFromSum(mPastBuckets[idx]);
+            mPastBuckets[idx] = nullptr;  // release (but not clear) the old bucket.
+        }
+    }
+
+    // It is an update operation.
+    if (latestPastBucketNum <= mMostRecentBucketNum &&
+        latestPastBucketNum > mMostRecentBucketNum - mNumOfPastPackets) {
+        subtractBucketFromSum(mPastBuckets[index(latestPastBucketNum)]);
+    }
+}
+
+void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+                                   const int64_t& bucketNum) {
+    flushPastBuckets(bucketNum);
+
+    auto& bucket = mPastBuckets[index(bucketNum)];
+    if (bucket == nullptr) {
+        bucket = std::make_shared<DimToValMap>();
+    }
+    bucket->insert({key, bucketValue});
+    addBucketToSum(bucket);
+    mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucketValues,
+                                   const int64_t& bucketNum) {
+    VLOG("addPastBucket() called.");
+    flushPastBuckets(bucketNum);
+    // Replace the oldest bucket with the new bucket we are adding.
+    mPastBuckets[index(bucketNum)] = bucketValues;
+    addBucketToSum(bucketValues);
+    mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
+    if (bucket == nullptr) {
+        return;
+    }
+    // For each dimension present in the bucket, subtract its value from its corresponding sum.
+    for (const auto& keyValuePair : *bucket) {
+        auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+        if (itr == mSumOverPastBuckets.end()) {
+            continue;
+        }
+        itr->second -= keyValuePair.second;
+        // TODO: No need to look up the object twice like this. Use a var.
+        if (itr->second == 0) {
+            mSumOverPastBuckets.erase(itr);
+        }
+    }
+}
+
+void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
+    if (bucket == nullptr) {
+        return;
+    }
+    // For each dimension present in the bucket, add its value to its corresponding sum.
+    for (const auto& keyValuePair : *bucket) {
+        mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+    }
+}
+
+int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+                                           const int64_t& bucketNum) const {
+    const auto& bucket = mPastBuckets[index(bucketNum)];
+    if (bucket == nullptr) {
+        return 0;
+    }
+    const auto& itr = bucket->find(key);
+    return itr == bucket->end() ? 0 : itr->second;
+}
+
+int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+    const auto& itr = mSumOverPastBuckets.find(key);
+    if (itr != mSumOverPastBuckets.end()) {
+        return itr->second;
+    }
+    return 0;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
+                                   const DimToValMap& currentBucket) {
+    if (currentBucketNum > mMostRecentBucketNum + 1) {
+        addPastBucket(nullptr, currentBucketNum - 1);
+    }
+    for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) {
+        if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) {
+            return true;
+        }
+    }
+    // In theory, we also need to check the dimsions not in the current bucket. In single-thread
+    // mode, usually we could avoid the following loops.
+    for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+        if (itr->second > mAlert.trigger_if_sum_gt()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+                                   const int64_t& currentBucketValue) {
+    if (currentBucketNum > mMostRecentBucketNum + 1) {
+        addPastBucket(key, 0, currentBucketNum - 1);
+    }
+    return getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
+}
+
+void AnomalyTracker::declareAnomaly(const uint64_t& timestamp) {
+    if (mLastAlarmTimestampNs >= 0 &&
+        timestamp - mLastAlarmTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC) {
+        VLOG("Skipping anomaly check since within refractory period");
+        return;
+    }
+    // TODO(guardrail): Consider guarding against too short refractory periods.
+    mLastAlarmTimestampNs = timestamp;
+
+    if (mAlert.has_incidentd_details()) {
+        // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+        ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+        // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+    } else {
+        ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+    }
+}
+
+void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+                                                  const uint64_t& timestamp) {
+    auto itr = mAlarms.find(dimensionKey);
+    if (itr == mAlarms.end()) {
+        return;
+    }
+
+    if (itr->second != nullptr &&
+        static_cast<uint32_t>(timestamp / NS_PER_SEC) >= itr->second->timestampSec) {
+        declareAnomaly(timestamp);
+        stopAlarm(dimensionKey);
+    }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+                                             const int64_t& currBucketNum,
+                                             const HashableDimensionKey& key,
+                                             const int64_t& currentBucketValue) {
+    if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
+        declareAnomaly(timestamp);
+    }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+                                             const int64_t& currBucketNum,
+                                             const DimToValMap& currentBucket) {
+    if (detectAnomaly(currBucketNum, currentBucket)) {
+        declareAnomaly(timestamp);
+    }
+}
+
+void AnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+                                const uint64_t& timestamp) {
+    sp<const AnomalyAlarm> alarm = new AnomalyAlarm{static_cast<uint32_t>(timestamp / NS_PER_SEC)};
+    mAlarms.insert({dimensionKey, alarm});
+    if (mAnomalyMonitor != nullptr) {
+        mAnomalyMonitor->add(alarm);
+    }
+}
+
+void AnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+    auto itr = mAlarms.find(dimensionKey);
+    if (itr != mAlarms.end()) {
+        mAlarms.erase(dimensionKey);
+    }
+    if (mAnomalyMonitor != nullptr) {
+        mAnomalyMonitor->remove(itr->second);
+    }
+}
+
+void AnomalyTracker::stopAllAlarms() {
+    std::set<HashableDimensionKey> keys;
+    for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
+        keys.insert(itr->first);
+    }
+    for (auto key : keys) {
+        stopAlarm(key);
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
new file mode 100644
index 0000000..ce6c995
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "AnomalyMonitor.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
+#include "stats_util.h"  // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class AnomalyTracker : public virtual RefBase {
+public:
+    AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs);
+
+    virtual ~AnomalyTracker();
+
+    // Adds a bucket.
+    // Bucket index starts from 0.
+    void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
+    void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+                       const int64_t& bucketNum);
+
+    // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+    bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket);
+    bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+                       const int64_t& currentBucketValue);
+
+    // Informs incidentd about the detected alert.
+    void declareAnomaly(const uint64_t& timestamp);
+
+    // Detects the alert and informs the incidentd when applicable.
+    void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+                                 const DimToValMap& currentBucket);
+    void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+                                 const HashableDimensionKey& key,
+                                 const int64_t& currentBucketValue);
+
+    // Starts the alarm at the given timestamp.
+    void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+    // Stops the alarm.
+    void stopAlarm(const HashableDimensionKey& dimensionKey);
+
+    // Stop all the alarms owned by this tracker.
+    void stopAllAlarms();
+
+    // Init the anmaly monitor which is shared across anomaly trackers.
+    inline void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+        mAnomalyMonitor = anomalyMonitor;
+    }
+
+    // Declares the anomaly when the alarm expired given the current timestamp.
+    void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+                                      const uint64_t& timestamp);
+
+    // Helper function to return the sum value of past buckets at given dimension.
+    int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+
+    // Helper function to return the value for a past bucket.
+    int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+
+    // Returns the anomaly threshold.
+    inline int64_t getAnomalyThreshold() const {
+        return mAlert.trigger_if_sum_gt();
+    }
+
+    // Helper function to return the last alarm timestamp.
+    inline int64_t getLastAlarmTimestampNs() const {
+        return mLastAlarmTimestampNs;
+    }
+
+    inline int getNumOfPastPackets() const {
+        return mNumOfPastPackets;
+    }
+
+protected:
+    void flushPastBuckets(const int64_t& currBucketNum);
+    // statsd_config.proto Alert message that defines this tracker.
+    const Alert mAlert;
+
+    // Bucket duration in ns.
+    int64_t mBucketSizeNs = 0;
+
+    // The number of past packets to track in the anomaly detection.
+    int mNumOfPastPackets = 0;
+
+    // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
+    // are still active.
+    std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+
+    // Anomaly alarm monitor.
+    sp<AnomalyMonitor> mAnomalyMonitor;
+
+    // The exisiting bucket list.
+    std::vector<shared_ptr<DimToValMap>> mPastBuckets;
+
+    // Sum over all existing buckets cached in mPastBuckets.
+    DimToValMap mSumOverPastBuckets;
+
+    // The bucket number of the last added bucket.
+    int64_t mMostRecentBucketNum = -1;
+
+    // The timestamp when the last anomaly was declared.
+    int64_t mLastAlarmTimestampNs = -1;
+
+    // Add the information in the given bucket to mSumOverPastBuckets.
+    void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
+
+    // Subtract the information in the given bucket from mSumOverPastBuckets
+    // and remove any items with value 0.
+    void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
+
+    // Calculates the corresponding bucket index within the circular array.
+    size_t index(int64_t bucketNum) const;
+
+    // Resets all data. For use when all the data gets stale.
+    void reset();
+
+    FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+    FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+    FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
deleted file mode 100644
index 6492177..0000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG true  // STOPSHIP if true
-#include "Log.h"
-
-#include "DiscreteAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
-    VLOG("DiscreteAnomalyTracker() called");
-    if (mAlert.number_of_buckets() <= 0) {
-        ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
-              (long long)mAlert.number_of_buckets());
-        return;
-    }
-    mPastBuckets.resize(mAlert.number_of_buckets());
-    reset(); // initialization
-}
-
-DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
-    VLOG("~DiscreteAnomalyTracker() called");
-}
-
-void DiscreteAnomalyTracker::reset() {
-    VLOG("reset() called.");
-    mPastBuckets.clear();
-    mPastBuckets.resize(mAlert.number_of_buckets());
-    mSumOverPastBuckets.clear();
-    mCurrentBucketIndex = -1;
-    mLastAlarmAtBucketIndex = -1;
-    mAnomalyDeclared = 0;
-}
-
-size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
-    return bucketNum % mAlert.number_of_buckets();
-}
-
-void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
-                                               int64_t bucketIndex) {
-    VLOG("addPastBucket() called.");
-    if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
-        ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
-        return;
-    }
-
-    // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
-    if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
-        mPastBuckets.clear();
-        mPastBuckets.resize(mAlert.number_of_buckets());
-        mSumOverPastBuckets.clear();
-    } else {
-        for (int64_t i = std::max(
-                     0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
-             i < bucketIndex - mAlert.number_of_buckets(); i++) {
-            const int idx = index(i);
-            subtractBucketFromSum(mPastBuckets[idx]);
-            mPastBuckets[idx] = nullptr;  // release (but not clear) the old bucket.
-        }
-    }
-    subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
-    mPastBuckets[index(bucketIndex)] = nullptr;  // release (but not clear) the old bucket.
-
-    // Replace the oldest bucket with the new bucket we are adding.
-    mPastBuckets[index(bucketIndex)] = BucketValues;
-    addBucketToSum(BucketValues);
-
-    mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
-}
-
-void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
-    if (bucket == nullptr) {
-        return;
-    }
-    // For each dimension present in the bucket, subtract its value from its corresponding sum.
-    for (const auto& keyValuePair : *bucket) {
-        auto itr = mSumOverPastBuckets.find(keyValuePair.first);
-        if (itr == mSumOverPastBuckets.end()) {
-            continue;
-        }
-        itr->second -= keyValuePair.second;
-        // TODO: No need to look up the object twice like this. Use a var.
-        if (itr->second == 0) {
-            mSumOverPastBuckets.erase(itr);
-        }
-    }
-}
-
-void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
-    if (bucket == nullptr) {
-        return;
-    }
-    // For each dimension present in the bucket, add its value to its corresponding sum.
-    for (const auto& keyValuePair : *bucket) {
-        mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
-    }
-}
-
-bool DiscreteAnomalyTracker::detectAnomaly() {
-    for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
-        if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
-    if (detectAnomaly()) {
-        declareAnomaly();
-    }
-}
-
-void DiscreteAnomalyTracker::declareAnomaly() {
-    if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
-                                        (long long)mAlert.refractory_period_in_buckets()) {
-        VLOG("Skipping anomaly check since within refractory period");
-        return;
-    }
-    mAnomalyDeclared++;
-    // TODO(guardrail): Consider guarding against too short refractory periods.
-    mLastAlarmAtBucketIndex = mCurrentBucketIndex;
-
-    if (mAlert.has_incidentd_details()) {
-        // TODO: Can construct a name based on the criteria (and/or relay the criteria).
-        ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
-        // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
-    } else {
-        ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
-    }
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
deleted file mode 100644
index ed7d5d7..0000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-#include "stats_util.h" // HashableDimensionKey and DimToValMap
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::shared_ptr;
-
-// This anomaly track assmues that all values are non-negative.
-class DiscreteAnomalyTracker {
- public:
-    DiscreteAnomalyTracker(const Alert& alert);
-
-    virtual ~DiscreteAnomalyTracker();
-
-    // Adds a new bucket or updates an existing bucket.
-    // Bucket index starts from 0.
-    void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
-
-    // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
-    bool detectAnomaly();
-
-    // Informs incidentd about the detected alert.
-    void declareAnomaly();
-
-    // Detects the alert and informs the incidentd when applicable.
-    void declareAndDeclareAnomaly();
-
-private:
-    // statsd_config.proto Alert message that defines this tracker.
-    const Alert mAlert;
-
-    // The exisiting bucket list.
-    std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
-
-    // Sum over all existing buckets cached in mPastBuckets.
-    DimToValMap mSumOverPastBuckets;
-
-    // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
-    int64_t mCurrentBucketIndex = -1;
-
-    // The bucket index when the last anomaly was declared.
-    int64_t mLastAlarmAtBucketIndex = -1;
-
-    // The total number of declared anomalies.
-    int64_t mAnomalyDeclared = 0;
-
-    // Add the information in the given bucket to mSumOverPastBuckets.
-    void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
-
-    // Subtract the information in the given bucket from mSumOverPastBuckets
-    // and remove any items with value 0.
-    void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
-
-    // Calculates the corresponding bucket index within the circular array.
-    size_t index(int64_t bucketNum);
-
-    // Resets all data. For use when all the data gets stale.
-    void reset();
-
-    FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
-    FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h
index 1a2e9c2..4982d4b 100644
--- a/cmds/statsd/src/anomaly/indexed_priority_queue.h
+++ b/cmds/statsd/src/anomaly/indexed_priority_queue.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include "Log.h"
-
 #include <utils/RefBase.h>
 #include <unordered_map>
 #include <vector>
@@ -28,7 +26,7 @@
 namespace os {
 namespace statsd {
 
-/** Defines a hash function for sp<AA>, returning the hash of the underlying pointer. */
+/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
 template <class AA>
 struct SpHash {
     size_t operator()(const sp<const AA>& k) const {
@@ -39,7 +37,7 @@
 /**
  * Min priority queue for generic type AA.
  * Unlike a regular priority queue, this class is also capable of removing interior elements.
- * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
+ * @tparam Comparator must implement [bool operator()(sp< AA> a, sp< AA> b)], returning
  *    whether a should be closer to the top of the queue than b.
  */
 template <class AA, class Comparator>
@@ -104,7 +102,6 @@
     if (!contains(a)) return;
     size_t idx = indices[a];
     if (idx >= pq.size()) {
-        ALOGE("indexed_priority_queue: Invalid index in map of indices.");
         return;
     }
     if (idx == size()) {  // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
@@ -193,7 +190,6 @@
 template <class AA, class Comparator>
 bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
     if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
-        ALOGE("indexed_priority_queue: Attempting to access invalid index");
         return false;  // got to do something.
     }
     return Comparator()(pq[idx1], pq[idx2]);
@@ -208,7 +204,6 @@
 template <class AA, class Comparator>
 void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
     if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
-        ALOGE("indexed_priority_queue: Attempting to swap invalid index");
         return;
     }
     sp<const AA> val_i = pq[i];
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ba93feb..57a92b6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -73,6 +73,7 @@
         SettingChanged setting_changed = 41;
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
         IsolatedUidChanged isolated_uid_changed = 43;
+        PacketWakeupOccurred packet_wakeup_occurred = 44;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -906,3 +907,32 @@
     optional uint64 freq_idx = 2;
     optional uint64 time_ms = 3;
 }
+
+/*
+ * Logs the reception of an incoming network packet causing the main system to wake up for
+ * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
+ * and processed by WakeupController.cpp.
+ */
+message PacketWakeupOccurred {
+    // The uid owning the socket into which the packet was delivered, or -1 if the packet was
+    // delivered nowhere.
+    optional int32 uid = 1;
+    // The interface name on which the packet was received.
+    optional string iface = 2;
+    // The ethertype value of the packet.
+    optional int32 ethertype = 3;
+    // String representation of the destination MAC address of the packet.
+    optional string destination_hardware_address = 4;
+    // String representation of the source address of the packet if this was an IP packet.
+    optional string source_ip = 5;
+    // String representation of the destination address of the packet if this was an IP packet.
+    optional string destination_ip = 6;
+    // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
+    // field if this was an IPv6 packet. The range of possible values is the same for both IP
+    // families.
+    optional int32 ip_next_header = 7;
+    // The source port if this was a TCP or UDP packet.
+    optional int32 source_port = 8;
+    // The destination port if this was a TCP or UDP packet.
+    optional int32 destination_port = 9;
+}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index f9da68e..d47bd4f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,7 +17,6 @@
 #define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 
-#include "../anomaly/DiscreteAnomalyTracker.h"
 #include "CountMetricProducer.h"
 #include "stats_util.h"
 
@@ -114,7 +113,7 @@
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
-    flushCounterIfNeeded(endTime);
+    flushIfNeeded(endTime);
     VLOG("metric %s dump report now...", mMetric.name().c_str());
 
     for (const auto& counter : mPastBuckets) {
@@ -170,7 +169,6 @@
 
     startNewProtoOutputStream(endTime);
     mPastBuckets.clear();
-    mByteSize = 0;
 
     return buffer;
 
@@ -188,7 +186,7 @@
         const LogEvent& event, bool scheduledPull) {
     uint64_t eventTimeNs = event.GetTimestampNs();
 
-    flushCounterIfNeeded(eventTimeNs);
+    flushIfNeeded(eventTimeNs);
 
     if (condition == false) {
         return;
@@ -205,41 +203,41 @@
         count++;
     }
 
-    VLOG("metric %s %s->%d", mMetric.name().c_str(), eventKey.c_str(),
-         (*mCurrentSlicedCounter)[eventKey]);
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+                                         mCurrentSlicedCounter->find(eventKey)->second);
+    }
+
+    VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+         (long long)(*mCurrentSlicedCounter)[eventKey]);
 }
 
 // When a new matched event comes in, we check if event falls into the current
 // bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
-    if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+    if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return;
     }
 
-    // adjust the bucket start time
-    // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
-    uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
     CountBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+    info.mBucketNum = mCurrentBucketNum;
     for (const auto& counter : *mCurrentSlicedCounter) {
         info.mCount = counter.second;
         auto& bucketList = mPastBuckets[counter.first];
         bucketList.push_back(info);
-        VLOG("metric %s, dump key value: %s -> %d", mMetric.name().c_str(), counter.first.c_str(),
-             counter.second);
-        mByteSize += sizeof(info);
+        VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(),
+             (long long)counter.second);
     }
 
     for (auto& tracker : mAnomalyTrackers) {
-        tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
-        tracker->declareAndDeclareAnomaly();
+        tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
     }
 
     // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
     mCurrentSlicedCounter = std::make_shared<DimToValMap>();
-
+    uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
     VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
@@ -250,7 +248,11 @@
 // greater than actual data size as it contains each dimension of
 // CountMetricData is  duplicated.
 size_t CountMetricProducer::byteSize() {
-    return mByteSize;
+    size_t totalSize = 0;
+    for (const auto& pair : mPastBuckets) {
+        totalSize += pair.second.size() * kBucketSize;
+    }
+    return totalSize;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b7e480c..b3f8ee3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -21,9 +21,9 @@
 
 #include <android/util/ProtoOutputStream.h>
 #include <gtest/gtest_prod.h>
+#include "../anomaly/AnomalyTracker.h"
 #include "../condition/ConditionTracker.h"
 #include "../matchers/matcher_util.h"
-#include "../anomaly/DiscreteAnomalyTracker.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
@@ -36,6 +36,7 @@
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     int64_t mCount;
+    uint64_t mBucketNum;
 };
 
 class CountMetricProducer : public MetricProducer {
@@ -50,6 +51,8 @@
 
     void finish() override;
 
+    void flushIfNeeded(const uint64_t newEventTime) override;
+
     // TODO: Pass a timestamp as a parameter in onDumpReport.
     std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
@@ -76,18 +79,15 @@
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
-    size_t mByteSize;
-
     // The current bucket.
     std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
 
-    vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
-
-    void flushCounterIfNeeded(const uint64_t newEventTime);
+    static const size_t kBucketSize = sizeof(CountBucket{});
 
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
+    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index eba2e06..b0a97b1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -110,16 +110,16 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        vector<DurationBucket>& bucket) {
+        const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
-            return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
-                                                     mCurrentBucketStartTimeNs, mBucketSizeNs,
-                                                     bucket);
+            return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+                                                     mNested, mCurrentBucketStartTimeNs,
+                                                     mBucketSizeNs, mAnomalyTrackers, bucket);
         case DurationMetric_AggregationType_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
-                                                   mCurrentBucketStartTimeNs, mBucketSizeNs,
-                                                   bucket);
+            return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+                                                   mNested, mCurrentBucketStartTimeNs,
+                                                   mBucketSizeNs, mAnomalyTrackers, bucket);
     }
 }
 
@@ -131,7 +131,6 @@
 void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
     // Now for each of the on-going event, check if the condition has changed for them.
-    flushIfNeeded(eventTime);
     for (auto& pair : mCurrentSlicedDuration) {
         pair.second->onSlicedConditionMayChange(eventTime);
     }
@@ -142,27 +141,11 @@
     mCondition = conditionMet;
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
-
-    flushIfNeeded(eventTime);
     for (auto& pair : mCurrentSlicedDuration) {
         pair.second->onConditionChanged(conditionMet, eventTime);
     }
 }
 
-static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& wrapper,
-                                       const vector<KeyValuePair>& key,
-                                       const vector<DurationBucketInfo>& buckets) {
-    DurationMetricData* data = wrapper.add_data();
-    for (const auto& kv : key) {
-        data->add_dimension()->CopyFrom(kv);
-    }
-    for (const auto& bucket : buckets) {
-        data->add_bucket_info()->CopyFrom(bucket);
-        VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
-             bucket.end_bucket_nanos(), bucket.duration_nanos());
-    }
-}
-
 std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
     long long endTime = time(nullptr) * NS_PER_SEC;
 
@@ -231,7 +214,6 @@
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return;
     }
-
     VLOG("flushing...........");
     for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
         if (it->second->flushIfNeeded(eventTime)) {
@@ -244,6 +226,7 @@
 
     int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+    mCurrentBucketNum += numBucketsForward;
 }
 
 void DurationMetricProducer::onMatchedLogEventInternal(
@@ -262,7 +245,7 @@
     HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
-        mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
+        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
     }
 
     auto it = mCurrentSlicedDuration.find(eventKey);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index bb5d4d9..4c8dbcb 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef DURATION_METRIC_PRODUCER_H
-#define DURATION_METRIC_PRODUCER_H
+#pragma once
+
 
 #include <unordered_map>
 
@@ -48,6 +48,7 @@
     void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
 
     void finish() override;
+    void flushIfNeeded(const uint64_t newEventTime) override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
     std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -95,11 +96,8 @@
     std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
             mCurrentSlicedDuration;
 
-    void flushDurationIfNeeded(const uint64_t newEventTime);
-
-    std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucket>& bucket);
-
-    void flushIfNeeded(uint64_t timestamp);
+    std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
+                                                           std::vector<DurationBucket>& bucket);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
 };
@@ -108,4 +106,3 @@
 }  // namespace os
 }  // namespace android
 
-#endif  // DURATION_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 0dccdf4..0c453cd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,8 @@
     void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
 
     void finish() override;
+    void flushIfNeeded(const uint64_t newEventTime) override {
+    }
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
     std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index ed18f89..42ac1a2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -63,8 +63,9 @@
 const int FIELD_ID_GAUGE = 3;
 
 GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int pullTagId)
-    : MetricProducer((time(nullptr) * NS_PER_SEC), conditionIndex, wizard),
+                                         const sp<ConditionWizard>& wizard, const int pullTagId,
+                                         const int64_t startTimeNs)
+    : MetricProducer(startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mPullTagId(pullTagId) {
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -114,7 +115,7 @@
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
-    flushGaugeIfNeededLocked(time(nullptr) * NS_PER_SEC);
+    flushIfNeeded(time(nullptr) * NS_PER_SEC);
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
@@ -168,7 +169,6 @@
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
     mPastBuckets.clear();
-    mByteSize = 0;
 
     return buffer;
 
@@ -178,15 +178,18 @@
 void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
     AutoMutex _l(mLock);
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+    flushIfNeeded(eventTime);
     mCondition = conditionMet;
 
-    // Push mode. Nothing to do.
+    // Push mode. No need to proactively pull the gauge data.
     if (mPullTagId == -1) {
         return;
     }
-    // If (1) the condition is not met or (2) we already pulled the gauge metric in the current
-    // bucket, do not pull gauge again.
-    if (!mCondition || mCurrentSlicedBucket.size() > 0) {
+    if (!mCondition) {
+        return;
+    }
+    // Already have gauge metric for the current bucket, do not do it again.
+    if (mCurrentSlicedBucket->size() > 0) {
         return;
     }
     vector<std::shared_ptr<LogEvent>> allData;
@@ -197,16 +200,16 @@
     for (const auto& data : allData) {
         onMatchedLogEvent(0, *data, false /*scheduledPull*/);
     }
-    flushGaugeIfNeededLocked(eventTime);
+    flushIfNeeded(eventTime);
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-long GaugeMetricProducer::getGauge(const LogEvent& event) {
+int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
     status_t err = NO_ERROR;
-    long val = event.GetLong(mMetric.gauge_field(), &err);
+    int64_t val = event.GetLong(mMetric.gauge_field(), &err);
     if (err == NO_ERROR) {
         return val;
     } else {
@@ -217,14 +220,9 @@
 
 void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
     AutoMutex mutex(mLock);
-    if (allData.size() == 0) {
-        return;
-    }
     for (const auto& data : allData) {
         onMatchedLogEvent(0, *data, true /*scheduledPull*/);
     }
-    uint64_t eventTime = allData.at(0)->GetTimestampNs();
-    flushGaugeIfNeededLocked(eventTime);
 }
 
 void GaugeMetricProducer::onMatchedLogEventInternal(
@@ -241,15 +239,21 @@
         return;
     }
 
-    // For gauge metric, we just simply use the latest guage in the given bucket.
-    const long gauge = getGauge(event);
-    if (gauge < 0) {
-        VLOG("Invalid gauge at event Time: %lld", (long long)eventTimeNs);
+    // When the event happens in a new bucket, flush the old buckets.
+    if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
+        flushIfNeeded(eventTimeNs);
+    }
+
+    // For gauge metric, we just simply use the first guage in the given bucket.
+    if (!mCurrentSlicedBucket->empty()) {
         return;
     }
-    mCurrentSlicedBucket[eventKey] = gauge;
-    if (mPullTagId < 0) {
-        flushGaugeIfNeededLocked(eventTimeNs);
+    const long gauge = getGauge(event);
+    if (gauge >= 0) {
+        (*mCurrentSlicedBucket)[eventKey] = gauge;
+    }
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
     }
 }
 
@@ -258,39 +262,45 @@
 // bucket.
 // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
 // the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushGaugeIfNeededLocked(const uint64_t eventTimeNs) {
-    if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
-        VLOG("event time is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
-             (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+    if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return;
     }
 
-    // Adjusts the bucket start time
-    int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
     GaugeBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+    info.mBucketNum = mCurrentBucketNum;
 
-    for (const auto& slice : mCurrentSlicedBucket) {
+    for (const auto& slice : *mCurrentSlicedBucket) {
         info.mGauge = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        mByteSize += sizeof(info);
-
-        VLOG("gauge metric %s, dump key value: %s -> %ld", mMetric.name().c_str(),
-             slice.first.c_str(), slice.second);
+        VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
+             slice.first.c_str(), (long long)slice.second);
     }
-    // Reset counters
-    mCurrentSlicedBucket.clear();
 
+    // Reset counters
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->addPastBucket(mCurrentSlicedBucket, mCurrentBucketNum);
+    }
+
+    mCurrentSlicedBucket = std::make_shared<DimToValMap>();
+
+    // Adjusts the bucket start time
+    int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+    mCurrentBucketNum += numBucketsForward;
     VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
 size_t GaugeMetricProducer::byteSize() {
-    return mByteSize;
+    size_t totalSize = 0;
+    for (const auto& pair : mPastBuckets) {
+        totalSize += pair.second.size() * kBucketSize;
+    }
+    return totalSize;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index f9e4deb..930afb2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -19,6 +19,7 @@
 #include <unordered_map>
 
 #include <android/util/ProtoOutputStream.h>
+#include <gtest/gtest_prod.h>
 #include "../condition/ConditionTracker.h"
 #include "../external/PullDataReceiver.h"
 #include "../external/StatsPullerManager.h"
@@ -35,6 +36,7 @@
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     int64_t mGauge;
+    uint64_t mBucketNum;
 };
 
 // This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -46,7 +48,8 @@
     // TODO: Pass in the start time from MetricsManager, it should be consistent
     // for all metrics.
     GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId);
+                        const sp<ConditionWizard>& wizard, const int pullTagId,
+                        const int64_t startTimeNs);
 
     virtual ~GaugeMetricProducer();
 
@@ -57,6 +60,7 @@
     void onSlicedConditionMayChange(const uint64_t eventTime) override;
 
     void finish() override;
+    void flushIfNeeded(const uint64_t newEventTime) override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
     std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -92,13 +96,15 @@
     std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current bucket.
-    std::unordered_map<HashableDimensionKey, long> mCurrentSlicedBucket;
+    std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
 
-    void flushGaugeIfNeededLocked(const uint64_t newEventTime);
+    int64_t getGauge(const LogEvent& event);
 
-    long getGauge(const LogEvent& event);
+    static const size_t kBucketSize = sizeof(GaugeBucket{});
 
-    size_t mByteSize;
+    FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c7982a8..0f93744 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,6 +17,7 @@
 #ifndef METRIC_PRODUCER_H
 #define METRIC_PRODUCER_H
 
+#include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
@@ -58,6 +59,7 @@
     // This is called when the metric collecting is done, e.g., when there is a new configuration
     // coming. MetricProducer should do the clean up, and dump existing data to dropbox.
     virtual void finish() = 0;
+    virtual void flushIfNeeded(const uint64_t newEventTime) = 0;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport and update all its
     // implementations.
@@ -72,6 +74,14 @@
     // state.
     virtual size_t byteSize() = 0;
 
+    void addAnomalyTracker(sp<AnomalyTracker> tracker) {
+        mAnomalyTrackers.push_back(tracker);
+    }
+
+    int64_t getBuckeSizeInNs() const {
+        return mBucketSizeNs;
+    }
+
 protected:
     const uint64_t mStartTimeNs;
 
@@ -97,6 +107,8 @@
 
     std::vector<EventConditionLink> mConditionLinks;
 
+    std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
     /*
      * Individual metrics can implement their own business logic here. All pre-processing is done.
      *
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e8a862f..5916b040 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -38,8 +38,8 @@
 
 MetricsManager::MetricsManager(const StatsdConfig& config) {
     mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
-                                    mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
-                                    mTrackerToConditionMap);
+                                    mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
+                                    mTrackerToMetricMap, mTrackerToConditionMap);
 }
 
 MetricsManager::~MetricsManager() {
@@ -150,6 +150,19 @@
     }
 }
 
+void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs,
+                                         sp<const AnomalyAlarm> anomaly) {
+    for (const auto& itr : mAllAnomalyTrackers) {
+        itr->declareAnomaly(timestampNs);
+    }
+}
+
+void MetricsManager::setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+    for (auto& itr : mAllAnomalyTrackers) {
+        itr->setAnomalyMonitor(anomalyMonitor);
+    }
+}
+
 // Returns the total byte size of all metrics managed by a single config source.
 size_t MetricsManager::byteSize() {
     size_t totalSize = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index fb16779..59ade7c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "logd/LogEvent.h"
@@ -43,6 +45,10 @@
     // Called when everything should wrap up. We are about to finish (e.g., new config comes).
     void finish();
 
+    void onAnomalyAlarmFired(const uint64_t timestampNs, sp<const AnomalyAlarm> anomaly);
+
+    void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
+
     // Config source owner can call onDumpReport() to get all the metrics collected.
     std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
 
@@ -70,6 +76,9 @@
     // Hold all metrics from the config.
     std::vector<sp<MetricProducer>> mAllMetricProducers;
 
+    // Hold all alert trackers.
+    std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index a35070b..9cbe6f6 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -188,7 +188,6 @@
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
     mPastBuckets.clear();
-    mByteSize = 0;
 
     return buffer;
 
@@ -215,7 +214,7 @@
             for (const auto& data : allData) {
                 onMatchedLogEvent(0, *data, false);
             }
-            flush_if_needed(eventTime);
+            flushIfNeeded(eventTime);
         }
         return;
     }
@@ -230,12 +229,12 @@
         uint64_t eventTime = allData.at(0)->GetTimestampNs();
         // alarm is not accurate and might drift.
         if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
-            flush_if_needed(eventTime);
+            flushIfNeeded(eventTime);
         }
         for (const auto& data : allData) {
             onMatchedLogEvent(0, *data, true);
         }
-        flush_if_needed(eventTime);
+        flushIfNeeded(eventTime);
     }
 }
 
@@ -282,7 +281,7 @@
             }
         }
     } else {
-        flush_if_needed(eventTimeNs);
+        flushIfNeeded(eventTimeNs);
         interval.raw.push_back(make_pair(value, 0));
     }
 }
@@ -298,18 +297,18 @@
     }
 }
 
-void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
+void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
         VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
              (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
         return;
     }
-
     VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
          (int)mCurrentSlicedBucket.size());
     ValueBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+    info.mBucketNum = mCurrentBucketNum;
 
     int tainted = 0;
     for (const auto& slice : mCurrentSlicedBucket) {
@@ -329,23 +328,29 @@
         // it will auto create new vector of ValuebucketInfo if the key is not found.
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        mByteSize += sizeof(info);
     }
 
     // Reset counters
     mCurrentSlicedBucket.swap(mNextSlicedBucket);
     mNextSlicedBucket.clear();
+
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+    mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+    mCurrentBucketNum += numBucketsForward;
+
     if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
     }
-    mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
 size_t ValueMetricProducer::byteSize() {
-    return mByteSize;
+    size_t totalSize = 0;
+    for (const auto& pair : mPastBuckets) {
+        totalSize += pair.second.size() * kBucketSize;
+    }
+    return totalSize;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c6c87f5..2b0b0ad 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -33,6 +33,7 @@
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     int64_t mValue;
+    uint64_t mBucketNum;
 };
 
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
@@ -46,6 +47,7 @@
     void onConditionChanged(const bool condition, const uint64_t eventTime) override;
 
     void finish() override;
+    void flushIfNeeded(const uint64_t eventTimeNs) override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
     std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -102,9 +104,7 @@
 
     long get_value(const LogEvent& event);
 
-    void flush_if_needed(const uint64_t eventTimeNs);
-
-    size_t mByteSize;
+    static const size_t kBucketSize = sizeof(ValueBucket{});
 
     FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 18b3349..c91ea0f 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -17,6 +17,7 @@
 #ifndef DURATION_TRACKER_H
 #define DURATION_TRACKER_H
 
+#include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "stats_util.h"
 
@@ -50,23 +51,28 @@
 };
 
 struct DurationBucket {
-    int64_t mBucketStartNs;
-    int64_t mBucketEndNs;
-    int64_t mDuration;
+    uint64_t mBucketStartNs;
+    uint64_t mBucketEndNs;
+    uint64_t mDuration;
+    uint64_t mBucketNum;
 };
 
 class DurationTracker {
 public:
-    DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
-                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+    DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                    int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+                    uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                     std::vector<DurationBucket>& bucket)
-        : mWizard(wizard),
+        : mEventKey(eventKey),
+          mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
           mBucket(bucket),
-          mDuration(0){};
+          mDuration(0),
+          mCurrentBucketNum(0),
+          mAnomalyTrackers(anomalyTrackers){};
     virtual ~DurationTracker(){};
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
@@ -79,7 +85,58 @@
     // events, so that the owner can safely remove the tracker.
     virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
 
+    // Predict the anomaly timestamp given the current status.
+    virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                              const uint64_t currentTimestamp) const = 0;
+
 protected:
+    // Starts the anomaly alarm.
+    void startAnomalyAlarm(const uint64_t eventTime) {
+        for (auto& anomalyTracker : mAnomalyTrackers) {
+            if (anomalyTracker != nullptr) {
+                anomalyTracker->startAlarm(mEventKey,
+                                           predictAnomalyTimestampNs(*anomalyTracker, eventTime));
+            }
+        }
+    }
+
+    // Stops the anomaly alarm.
+    void stopAnomalyAlarm() {
+        for (auto& anomalyTracker : mAnomalyTrackers) {
+            if (anomalyTracker != nullptr) {
+                anomalyTracker->stopAlarm(mEventKey);
+            }
+        }
+    }
+
+    void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
+        for (auto& anomalyTracker : mAnomalyTrackers) {
+            if (anomalyTracker != nullptr) {
+                anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
+            }
+        }
+    }
+
+    void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+                                 const int64_t& currentBucketValue) {
+        for (auto& anomalyTracker : mAnomalyTrackers) {
+            if (anomalyTracker != nullptr) {
+                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
+                                                        currentBucketValue);
+            }
+        }
+    }
+
+    void declareAnomalyIfAlarmExpired(const uint64_t& timestamp) {
+        for (auto& anomalyTracker : mAnomalyTrackers) {
+            if (anomalyTracker != nullptr) {
+                anomalyTracker->declareAnomalyIfAlarmExpired(mEventKey, timestamp);
+            }
+        }
+    }
+
+    HashableDimensionKey mEventKey;
+
     sp<ConditionWizard> mWizard;
 
     const int mConditionTrackerIndex;
@@ -93,6 +150,13 @@
     std::vector<DurationBucket>& mBucket;  // where to write output
 
     int64_t mDuration;  // current recorded duration result
+
+    uint64_t mCurrentBucketNum;
+
+    std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
+    FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 8c7bfb6..06e743d 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,14 +23,18 @@
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
+                                       sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                        std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket) {
+    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+                      anomalyTrackers, bucket) {
 }
 
 void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                    const uint64_t eventTime, const ConditionKey& conditionKey) {
+    flushIfNeeded(eventTime);
     // this will construct a new DurationInfo if this key didn't exist.
     DurationInfo& duration = mInfos[key];
     duration.conditionKeys = conditionKey;
@@ -56,8 +60,11 @@
     }
 }
 
+
 void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
                                   bool forceStop) {
+    flushIfNeeded(eventTime);
+    declareAnomalyIfAlarmExpired(eventTime);
     VLOG("MaxDuration: key %s stop", key.c_str());
     if (mInfos.find(key) == mInfos.end()) {
         // we didn't see a start event before. do nothing.
@@ -78,6 +85,7 @@
                      (long long)duration.lastStartTime, (long long)eventTime,
                      (long long)durationTime);
                 duration.lastDuration = duration.lastDuration + durationTime;
+                duration.lastStartTime = -1;
                 VLOG("  record duration: %lld ", (long long)duration.lastDuration);
             }
             break;
@@ -93,6 +101,7 @@
 
     if (duration.lastDuration > mDuration) {
         mDuration = duration.lastDuration;
+        detectAndDeclareAnomaly(eventTime, mCurrentBucketNum, mDuration);
         VLOG("Max: new max duration: %lld", (long long)mDuration);
     }
     // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
@@ -101,9 +110,14 @@
         mInfos.erase(key);
     }
 }
+
 void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
-    for (auto& pair : mInfos) {
-        noteStop(pair.first, eventTime, true);
+    std::set<HashableDimensionKey> keys;
+    for (const auto& pair : mInfos) {
+        keys.insert(pair.first);
+    }
+    for (auto& key : keys) {
+        noteStop(key, eventTime, true);
     }
 }
 
@@ -122,7 +136,7 @@
     DurationBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = endTime;
-
+    info.mBucketNum = mCurrentBucketNum;
 
     uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
     mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
@@ -165,6 +179,7 @@
     if (mDuration != 0) {
         info.mDuration = mDuration;
         mBucket.push_back(info);
+        addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
         VLOG("  final duration for last bucket: %lld", (long long)mDuration);
     }
 
@@ -174,11 +189,15 @@
             DurationBucket info;
             info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
             info.mBucketEndNs = endTime + mBucketSizeNs * i;
+            info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
             mBucket.push_back(info);
+            addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
             VLOG("  filling gap bucket with duration %lld", (long long)mBucketSizeNs);
         }
     }
+
+    mCurrentBucketNum += numBucketsForward;
     // If this tracker has no pending events, tell owner to remove.
     return !hasPendingEvent;
 }
@@ -204,6 +223,8 @@
 
 void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                                               const uint64_t timestamp) {
+    flushIfNeeded(timestamp);
+    declareAnomalyIfAlarmExpired(timestamp);
     auto it = mInfos.find(key);
     if (it == mInfos.end()) {
         return;
@@ -215,7 +236,6 @@
             if (!conditionMet) {
                 it->second.state = DurationState::kPaused;
                 it->second.lastDuration += (timestamp - it->second.lastStartTime);
-
                 VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
             }
             break;
@@ -232,6 +252,16 @@
             }
             break;
     }
+    if (it->second.lastDuration > mDuration) {
+        mDuration = it->second.lastDuration;
+        detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+    }
+}
+
+int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                                      const uint64_t currentTimestamp) const {
+    ALOGE("Max duration producer does not support anomaly timestamp prediction!!!");
+    return currentTimestamp;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 167f81e..ca10210 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,8 +28,10 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
-                       uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+    MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                       int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+                       uint64_t bucketSizeNs,
+                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                        std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
@@ -40,11 +42,20 @@
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
+    int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                      const uint64_t currentTimestamp) const override;
+
 private:
     std::map<HashableDimensionKey, DurationInfo> mInfos;
 
     void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                               const uint64_t timestamp);
+
+    FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
+    FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
+    FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
+    FRIEND_TEST(MaxDurationTrackerTest, TestStopAll);
+    FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index faf5ce5..29b6c89 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -23,11 +23,14 @@
 
 using std::pair;
 
-OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
+                                           sp<ConditionWizard> wizard, int conditionIndex,
                                            bool nesting, uint64_t currentBucketStartNs,
                                            uint64_t bucketSizeNs,
+                                           const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                            std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket),
+    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+                      anomalyTrackers, bucket),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -35,10 +38,12 @@
 
 void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                      const uint64_t eventTime, const ConditionKey& conditionKey) {
+    flushIfNeeded(eventTime);
     if (condition) {
         if (mStarted.size() == 0) {
             mLastStartTime = eventTime;
             VLOG("record first start....");
+            startAnomalyAlarm(eventTime);
         }
         mStarted[key]++;
     } else {
@@ -54,6 +59,8 @@
 
 void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
                                     const bool stopAll) {
+    flushIfNeeded(timestamp);
+    declareAnomalyIfAlarmExpired(timestamp);
     VLOG("Oring: %s stop", key.c_str());
     auto it = mStarted.find(key);
     if (it != mStarted.end()) {
@@ -64,6 +71,8 @@
         }
         if (mStarted.empty()) {
             mDuration += (timestamp - mLastStartTime);
+            detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+            mLastStartTime = -1;
             VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
                  (long long)mDuration);
         }
@@ -76,56 +85,70 @@
             mPaused.erase(pausedIt);
             mConditionKeyMap.erase(key);
         }
-        }
+    }
+    if (mStarted.empty()) {
+        stopAnomalyAlarm();
+    }
 }
+
 void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
+    flushIfNeeded(timestamp);
+    declareAnomalyIfAlarmExpired(timestamp);
     if (!mStarted.empty()) {
         mDuration += (timestamp - mLastStartTime);
         VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
              (long long)mDuration);
+        detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
     }
 
+    stopAnomalyAlarm();
     mStarted.clear();
     mPaused.clear();
     mConditionKeyMap.clear();
+    mLastStartTime = -1;
 }
 
 bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
-    if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+    if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return false;
     }
     VLOG("OringDurationTracker Flushing.............");
     // adjust the bucket start time
     int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-    DurationBucket info;
-    uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
-    info.mBucketStartNs = mCurrentBucketStartTimeNs;
-    info.mBucketEndNs = endTime;
-
-    uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
-    mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
-
+    DurationBucket current_info;
+    current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+    current_info.mBucketEndNs = current_info.mBucketStartNs + mBucketSizeNs;
+    current_info.mBucketNum = mCurrentBucketNum;
+    // Process the current bucket.
     if (mStarted.size() > 0) {
-        mDuration += (endTime - mLastStartTime);
+        mDuration += (current_info.mBucketEndNs - mLastStartTime);
+        mLastStartTime = current_info.mBucketEndNs;
     }
-    if (mDuration != 0) {
-        info.mDuration = mDuration;
-        // it will auto create new vector of CountbucketInfo if the key is not found.
-        mBucket.push_back(info);
-        VLOG("  duration: %lld", (long long)mDuration);
+    if (mDuration > 0) {
+        current_info.mDuration = mDuration;
+        mBucket.push_back(current_info);
+        addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
+        VLOG("  duration: %lld", (long long)current_info.mDuration);
     }
 
     if (mStarted.size() > 0) {
         for (int i = 1; i < numBucketsForward; i++) {
             DurationBucket info;
-            info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
-            info.mBucketEndNs = endTime + mBucketSizeNs * i;
+            info.mBucketStartNs = mCurrentBucketStartTimeNs + mBucketSizeNs * i;
+            info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
+            info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-            mBucket.push_back(info);
-            VLOG("  add filling bucket with duration %lld", (long long)mBucketSizeNs);
+            mLastStartTime = info.mBucketEndNs;
+            if (info.mDuration > 0) {
+                mBucket.push_back(info);
+                addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
+                VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
+            }
         }
     }
-    mLastStartTime = mCurrentBucketStartTimeNs;
+    mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+    mCurrentBucketNum += numBucketsForward;
+
     mDuration = 0;
 
     // if all stopped, then tell owner it's safe to remove this tracker.
@@ -133,6 +156,8 @@
 }
 
 void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+    flushIfNeeded(timestamp);
+    declareAnomalyIfAlarmExpired(timestamp);
     vector<pair<HashableDimensionKey, int>> startedToPaused;
     vector<pair<HashableDimensionKey, int>> pausedToStarted;
     if (!mStarted.empty()) {
@@ -154,9 +179,11 @@
         }
 
         if (mStarted.empty()) {
-            mDuration += (timestamp - mLastStartTime);
+            mDuration = (timestamp - mLastStartTime);
+            mLastStartTime = -1;
             VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
                  (long long)mDuration);
+            detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
         }
     }
 
@@ -183,26 +210,95 @@
         }
     }
 
+    if (mStarted.empty() && !pausedToStarted.empty()) {
+        startAnomalyAlarm(timestamp);
+    }
     mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
     mPaused.insert(startedToPaused.begin(), startedToPaused.end());
+
+    if (mStarted.empty()) {
+        stopAnomalyAlarm();
+    }
 }
 
 void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+    flushIfNeeded(timestamp);
+    declareAnomalyIfAlarmExpired(timestamp);
     if (condition) {
         if (!mPaused.empty()) {
             VLOG("Condition true, all started");
             if (mStarted.empty()) {
-                mLastStartTime = timestamp;
+                mLastStartTime = -1;
+            }
+            if (mStarted.empty() && !mPaused.empty()) {
+                startAnomalyAlarm(timestamp);
             }
             mStarted.insert(mPaused.begin(), mPaused.end());
+            mPaused.clear();
         }
     } else {
         if (!mStarted.empty()) {
             VLOG("Condition false, all paused");
-            mDuration += (timestamp - mLastStartTime);
+            mDuration = (timestamp - mLastStartTime);
+            mLastStartTime = -1;
             mPaused.insert(mStarted.begin(), mStarted.end());
+            mStarted.clear();
+            detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
         }
     }
+    if (mStarted.empty()) {
+        stopAnomalyAlarm();
+    }
+}
+
+int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                                        const uint64_t eventTimestampNs) const {
+    // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
+    // All variables below represent durations (not timestamps).
+
+    // The time until the current bucket ends. This is how much more 'space' it can hold.
+    const int64_t currRemainingBucketSizeNs =
+            mBucketSizeNs - (eventTimestampNs - mCurrentBucketStartTimeNs);
+    // TODO: This should never be < 0. Document/guard against possible failures if it is.
+
+    const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
+
+    // As we move into the future, old buckets get overwritten (so their old data is erased).
+
+    // Sum of past durations. Will change as we overwrite old buckets.
+    int64_t pastNs = mDuration;
+    pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
+
+    // How much of the threshold is still unaccounted after considering pastNs.
+    int64_t leftNs = thresholdNs - pastNs;
+
+    // First deal with the remainder of the current bucket.
+    if (leftNs <= currRemainingBucketSizeNs) {  // Predict the anomaly will occur in this bucket.
+        return eventTimestampNs + leftNs;
+    }
+    // The remainder of this bucket contributes, but we must then move to the next bucket.
+    pastNs += currRemainingBucketSizeNs;
+
+    // Now deal with the past buckets, starting with the oldest.
+    for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastPackets();
+         futBucketIdx++) {
+        // We now overwrite the oldest bucket with the previous 'current', and start a new
+        // 'current'.
+        pastNs -= anomalyTracker.getPastBucketValue(
+                mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastPackets() + futBucketIdx);
+        leftNs = thresholdNs - pastNs;
+        if (leftNs <= mBucketSizeNs) {  // Predict anomaly will occur in this bucket.
+            return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) +
+                   leftNs;
+        } else {  // This bucket would be entirely filled, and we'll need to move to the next
+                  // bucket.
+            pastNs += mBucketSizeNs;
+        }
+    }
+
+    // If we have reached this point, we even have to overwrite the the original current bucket.
+    // Thus, none of the past data will still be extant - pastNs is now 0.
+    return eventTimestampNs + thresholdNs;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 78760ba..6f92113 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,8 +27,10 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
-                         uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+    OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                         int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+                         uint64_t bucketSizeNs,
+                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                          std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
@@ -39,6 +41,9 @@
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
     bool flushIfNeeded(uint64_t timestampNs) override;
 
+    int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                      const uint64_t currentTimestamp) const override;
+
 private:
     // We don't need to keep track of individual durations. The information that's needed is:
     // 1) which keys are started. We record the first start time.
@@ -49,6 +54,12 @@
     std::map<HashableDimensionKey, int> mPaused;
     int64_t mLastStartTime;
     std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+
+    FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
+    FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
+    FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
+    FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index d83c144..2344cb4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -190,7 +190,8 @@
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, std::vector<int>>& conditionToMetricMap,
-                 unordered_map<int, std::vector<int>>& trackerToMetricMap) {
+                 unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                 unordered_map<string, int>& metricMap) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
                                 config.event_metric_size() + config.value_metric_size();
@@ -208,6 +209,7 @@
         }
 
         int metricIndex = allMetricProducers.size();
+        metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
                                          allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -236,6 +238,7 @@
     for (int i = 0; i < config.duration_metric_size(); i++) {
         int metricIndex = allMetricProducers.size();
         const DurationMetric& metric = config.duration_metric(i);
+        metricMap.insert({metric.name(), metricIndex});
 
         auto what_it = conditionTrackerMap.find(metric.what());
         if (what_it == conditionTrackerMap.end()) {
@@ -305,6 +308,7 @@
     for (int i = 0; i < config.event_metric_size(); i++) {
         int metricIndex = allMetricProducers.size();
         const EventMetric& metric = config.event_metric(i);
+        metricMap.insert({metric.name(), metricIndex});
         if (!metric.has_name() || !metric.has_what()) {
             ALOGW("cannot find the metric name or what in config");
             return false;
@@ -342,6 +346,7 @@
         }
 
         int metricIndex = allMetricProducers.size();
+        metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
                                          allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -387,6 +392,7 @@
         }
 
         int metricIndex = allMetricProducers.size();
+        metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
                                          allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -419,21 +425,43 @@
         }
 
         sp<MetricProducer> gaugeProducer =
-                new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId);
+                new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(gaugeProducer);
     }
     return true;
 }
 
+bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap,
+                vector<sp<MetricProducer>>& allMetricProducers,
+                vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+    for (int i = 0; i < config.alert_size(); i++) {
+        const Alert& alert = config.alert(i);
+        const auto& itr = metricProducerMap.find(alert.metric_name());
+        if (itr == metricProducerMap.end()) {
+            ALOGW("alert has unknown metric name: %s %s", alert.name().c_str(),
+                  alert.metric_name().c_str());
+            return false;
+        }
+        const int metricIndex = itr->second;
+        sp<AnomalyTracker> anomalyTracker =
+                new AnomalyTracker(alert, allMetricProducers[metricIndex]->getBuckeSizeInNs());
+        allMetricProducers[metricIndex]->addAnomalyTracker(anomalyTracker);
+        allAnomalyTrackers.push_back(anomalyTracker);
+    }
+    return true;
+}
+
 bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
+                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToConditionMap) {
     unordered_map<string, int> logTrackerMap;
     unordered_map<string, int> conditionTrackerMap;
+    unordered_map<string, int> metricProducerMap;
 
     if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
@@ -449,10 +477,14 @@
 
     if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
                      allConditionTrackers, allMetricProducers, conditionToMetricMap,
-                     trackerToMetricMap)) {
+                     trackerToMetricMap, metricProducerMap)) {
         ALOGE("initMetricProducers failed");
         return false;
     }
+    if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) {
+        ALOGE("initAlerts failed");
+        return false;
+    }
     return true;
 }
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index edf3af0..7d7e0c3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -88,6 +88,7 @@
                       std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
+                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index e1d0aceb..b7d8f97 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -35,15 +35,14 @@
 
 typedef std::map<std::string, HashableDimensionKey> ConditionKey;
 
-// TODO: For P, change int to int64_t.
-// TODO: Should HashableDimensionKey be marked here as const?
-typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
 
 EventMetricData parse(log_msg msg);
 
 int getTagId(log_msg msg);
 
 std::string getHashableKey(std::vector<KeyValuePair> key);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index d3b04ba..e75a37f 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -113,11 +113,11 @@
 }
 
 message EventConditionLink {
-  optional string condition = 1;
+    optional string condition = 1;
 
-  repeated KeyMatcher key_in_main = 2;
+    repeated KeyMatcher key_in_main = 2;
 
-  repeated KeyMatcher key_in_condition = 3;
+    repeated KeyMatcher key_in_condition = 3;
 }
 
 message EventMetric {
@@ -217,8 +217,6 @@
     optional int32 refractory_period_secs = 5;
 
     optional int64 trigger_if_sum_gt = 6;
-
-    optional int32 refractory_period_in_buckets = 7;
 }
 
 message StatsdConfig {
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp
index 59fa160..920ca08 100644
--- a/cmds/statsd/tests/AnomalyMonitor_test.cpp
+++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp
@@ -20,6 +20,8 @@
 
 #ifdef __ANDROID__
 TEST(AnomalyMonitor, popSoonerThan) {
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
     unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
     AnomalyMonitor am(2);
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index caa1cf4..3dd4e70 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -34,6 +34,7 @@
 using std::set;
 using std::unordered_map;
 using std::vector;
+using android::os::statsd::Condition;
 
 #ifdef __ANDROID__
 
@@ -71,6 +72,19 @@
     combination->add_matcher("SCREEN_IS_ON");
     combination->add_matcher("SCREEN_IS_OFF");
 
+    CountMetric* metric = config.add_count_metric();
+    metric->set_name("3");
+    metric->set_what("SCREEN_IS_ON");
+    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    KeyMatcher* keyMatcher = metric->add_dimension();
+    keyMatcher->set_key(1);
+
+    auto alert = config.add_alert();
+    alert->set_name("3");
+    alert->set_metric_name("3");
+    alert->set_number_of_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
     return config;
 }
 
@@ -100,6 +114,29 @@
     return config;
 }
 
+StatsdConfig buildAlertWithUnknownMetric() {
+    StatsdConfig config;
+    config.set_name("12345");
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_name("3");
+    metric->set_what("SCREEN_IS_ON");
+    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    KeyMatcher* keyMatcher = metric->add_dimension();
+    keyMatcher->set_key(1);
+
+    auto alert = config.add_alert();
+    alert->set_name("3");
+    alert->set_metric_name("2");
+    alert->set_number_of_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
 StatsdConfig buildMissingMatchers() {
     StatsdConfig config;
     config.set_name("12345");
@@ -156,6 +193,12 @@
     KeyMatcher* keyMatcher = metric->add_dimension();
     keyMatcher->set_key(1);
 
+    auto alert = config.add_alert();
+    alert->set_name("3");
+    alert->set_metric_name("3");
+    alert->set_number_of_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
     return config;
 }
 
@@ -183,7 +226,7 @@
     simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
-    Condition* condition = config.add_condition();
+    auto condition = config.add_condition();
     condition->set_name("SCREEN_IS_ON");
     SimpleCondition* simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("SCREEN_IS_ON");
@@ -206,13 +249,16 @@
     vector<sp<LogMatchingTracker>> allLogEntryMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
     EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                 allMetricProducers, conditionToMetricMap, trackerToMetricMap,
-                                 trackerToConditionMap));
+                                 allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                 trackerToMetricMap, trackerToConditionMap));
+    EXPECT_EQ(1u, allMetricProducers.size());
+    EXPECT_EQ(1u, allAnomalyTrackers.size());
 }
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -221,13 +267,14 @@
     vector<sp<LogMatchingTracker>> allLogEntryMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
     EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
-                                  trackerToConditionMap));
+                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -236,13 +283,14 @@
     vector<sp<LogMatchingTracker>> allLogEntryMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
     EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
-                                  trackerToConditionMap));
+                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -251,13 +299,13 @@
     vector<sp<LogMatchingTracker>> allLogEntryMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-
     EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
-                                  trackerToConditionMap));
+                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleConditionDependency) {
@@ -266,13 +314,30 @@
     vector<sp<LogMatchingTracker>> allLogEntryMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
     EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
-                                  trackerToConditionMap));
+                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+    StatsdConfig config = buildAlertWithUnknownMetric();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap));
 }
 
 #else
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 4c12b03..0c19468 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -34,7 +34,8 @@
 
 TEST(UidMapTest, TestIsolatedUID) {
     sp<UidMap> m = new UidMap();
-    StatsLogProcessor p(m, nullptr);
+    sp<AnomalyMonitor> anomalyMonitor;
+    StatsLogProcessor p(m, anomalyMonitor, nullptr);
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index b8150d0..e0200f27 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/anomaly/DiscreteAnomalyTracker.h"
+#include "src/anomaly/AnomalyTracker.h"
 
 #include <gtest/gtest.h>
 #include <stdio.h>
@@ -37,7 +37,7 @@
     }
 }
 
-std::shared_ptr<DimToValMap> MockeBucket(
+std::shared_ptr<DimToValMap> MockBucket(
         const std::vector<std::pair<string, long>>& key_value_pair_list) {
     std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
     AddValueToBucket(key_value_pair_list, bucket);
@@ -45,190 +45,240 @@
 }
 
 TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+    const int64_t bucketSizeNs = 30 * NS_PER_SEC;
     Alert alert;
     alert.set_number_of_buckets(3);
-    alert.set_refractory_period_in_buckets(3);
+    alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
-    DiscreteAnomalyTracker anomaly_tracker(alert);
+    AnomalyTracker anomalyTracker(alert, bucketSizeNs);
 
-    std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
-    // Adds bucket #0
-    anomaly_tracker.addOrUpdateBucket(bucket0, 0);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+    std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+    int64_t eventTimestamp0 = 10;
+    std::shared_ptr<DimToValMap> bucket1 = MockBucket({{"a", 1}});
+    int64_t eventTimestamp1 = bucketSizeNs + 11;
+    std::shared_ptr<DimToValMap> bucket2 = MockBucket({{"b", 1}});
+    int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
+    std::shared_ptr<DimToValMap> bucket3 = MockBucket({{"a", 2}});
+    int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
+    std::shared_ptr<DimToValMap> bucket4 = MockBucket({{"b", 1}});
+    int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
+    std::shared_ptr<DimToValMap> bucket5 = MockBucket({{"a", 2}});
+    int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
+    std::shared_ptr<DimToValMap> bucket6 = MockBucket({{"a", 2}});
+    int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
 
-    // Adds bucket #0 again. The sum does not change.
-    anomaly_tracker.addOrUpdateBucket(bucket0, 0);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(0, *bucket0));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp0, 0, *bucket0);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
 
-    // Adds bucket #1.
-    std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
-    anomaly_tracker.addOrUpdateBucket(bucket1, 1);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    // Alarm.
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+    // Adds past bucket #0
+    anomalyTracker.addPastBucket(bucket0, 0);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
 
-    // Adds bucket #1 again. The sum does not change.
-    anomaly_tracker.addOrUpdateBucket(bucket1, 1);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    // Alarm.
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+    // Adds past bucket #0 again. The sum does not change.
+    anomalyTracker.addPastBucket(bucket0, 0);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
 
-    // Adds bucket #2.
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    // Adds past bucket #1.
+    anomalyTracker.addPastBucket(bucket1, 1);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+    // Adds past bucket #1 again. Nothing changes.
+    anomalyTracker.addPastBucket(bucket1, 1);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+    // Adds past bucket #2.
+    anomalyTracker.addPastBucket(bucket2, 2);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
     // Within refractory period.
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
 
     // Adds bucket #3.
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
-    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+    anomalyTracker.addPastBucket(bucket3, 3L);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
 
-    // Adds bucket #3.
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    // Within refractory period.
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+    // Adds bucket #4.
+    anomalyTracker.addPastBucket(bucket4, 4);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
 
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    // Adds bucket #5.
+    anomalyTracker.addPastBucket(bucket5, 5);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
     // Within refractory period.
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
 }
 
 TEST(AnomalyTrackerTest, TestSparseBuckets) {
+    const int64_t bucketSizeNs = 30 * NS_PER_SEC;
     Alert alert;
     alert.set_number_of_buckets(3);
-    alert.set_refractory_period_in_buckets(3);
+    alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
-    DiscreteAnomalyTracker anomaly_tracker(alert);
+    AnomalyTracker anomalyTracker(alert, bucketSizeNs);
 
-    // Add bucket #9
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+    std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+    std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
+    std::shared_ptr<DimToValMap> bucket18 = MockBucket({{"b", 1}, {"c", 1}});
+    std::shared_ptr<DimToValMap> bucket20 = MockBucket({{"b", 3}, {"c", 1}});
+    std::shared_ptr<DimToValMap> bucket25 = MockBucket({{"d", 1}});
+    std::shared_ptr<DimToValMap> bucket28 = MockBucket({{"e", 2}});
 
-    // Add bucket #16
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+    int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
+    int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
+    int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
+    int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
+    int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
+    int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
 
-    // Add bucket #18
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(9, *bucket9));
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 9, *bucket9);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1);
+
+    // Add past bucket #9
+    anomalyTracker.addPastBucket(bucket9, 9);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 16, *bucket16);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+
+    // Add past bucket #16
+    anomalyTracker.addPastBucket(bucket16, 16);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
     // Within refractory period.
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
 
-    // Add bucket #18 again.
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+    // Add past bucket #18
+    anomalyTracker.addPastBucket(bucket18, 18);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
 
-    // Add bucket #20
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+    // Add bucket #18 again. Nothing changes.
+    anomalyTracker.addPastBucket(bucket18, 18);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
+    // Within refractory period.
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
 
-    // Add bucket #25
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
-    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+    // Add past bucket #20
+    anomalyTracker.addPastBucket(bucket20, 20);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 3LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 25, *bucket25);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
 
-    // Add bucket #28
-    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
-    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
-    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
-    anomaly_tracker.declareAndDeclareAnomaly();
-    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
-    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+    // Add past bucket #25
+    anomalyTracker.addPastBucket(bucket25, 25);
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("d"), 1LL);
+    EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 28, *bucket28);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+    // Updates current bucket #28.
+    (*bucket28)["e"] = 5;
+    EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
+    EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6 + 7, 28, *bucket28);
+    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp6 + 7);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index 600b953..d6cd876 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -22,10 +22,12 @@
 
 /** struct for template in indexed_priority_queue */
 struct AATest : public RefBase {
-    AATest(uint32_t val) : val(val) {
+    AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) {
     }
 
     const int val;
+    const std::string a;
+    const std::string b;
 
     struct Smaller {
         bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
@@ -36,9 +38,11 @@
 
 #ifdef __ANDROID__
 TEST(indexed_priority_queue, empty_and_size) {
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> aa4 = new AATest{4};
-    sp<const AATest> aa8 = new AATest{8};
+    sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
 
     EXPECT_EQ(0u, ipq.size());
     EXPECT_TRUE(ipq.empty());
@@ -61,13 +65,15 @@
 }
 
 TEST(indexed_priority_queue, top) {
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> aa2 = new AATest{2};
-    sp<const AATest> aa4 = new AATest{4};
-    sp<const AATest> aa8 = new AATest{8};
-    sp<const AATest> aa12 = new AATest{12};
-    sp<const AATest> aa16 = new AATest{16};
-    sp<const AATest> aa20 = new AATest{20};
+    sp<const AATest> aa2 = new AATest{2, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa12 = new AATest{12, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa16 = new AATest{16, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa20 = new AATest{20, emptyMetricId, emptyDimensionId};
 
     EXPECT_EQ(ipq.top(), nullptr);
 
@@ -113,9 +119,11 @@
 }
 
 TEST(indexed_priority_queue, push_same_aa) {
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> aa4_a = new AATest{4};
-    sp<const AATest> aa4_b = new AATest{4};
+    sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
 
     ipq.push(aa4_a);
     EXPECT_EQ(1u, ipq.size());
@@ -134,9 +142,11 @@
 }
 
 TEST(indexed_priority_queue, remove_nonexistant) {
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> aa4 = new AATest{4};
-    sp<const AATest> aa5 = new AATest{5};
+    sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa5 = new AATest{5, emptyMetricId, emptyDimensionId};
 
     ipq.push(aa4);
     ipq.remove(aa5);
@@ -147,8 +157,10 @@
 
 TEST(indexed_priority_queue, remove_same_aa) {
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> aa4_a = new AATest{4};
-    sp<const AATest> aa4_b = new AATest{4};
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
+    sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+    sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
 
     ipq.push(aa4_a);
     ipq.push(aa4_b);
@@ -184,9 +196,11 @@
 
 TEST(indexed_priority_queue, pop) {
     indexed_priority_queue<AATest, AATest::Smaller> ipq;
-    sp<const AATest> a = new AATest{1};
-    sp<const AATest> b = new AATest{2};
-    sp<const AATest> c = new AATest{3};
+    std::string emptyMetricId;
+    std::string emptyDimensionId;
+    sp<const AATest> a = new AATest{1, emptyMetricId, emptyDimensionId};
+    sp<const AATest> b = new AATest{2, emptyMetricId, emptyDimensionId};
+    sp<const AATest> c = new AATest{3, emptyMetricId, emptyDimensionId};
 
     ipq.push(c);
     ipq.push(b);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index b7c9b40..35e08af 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -54,21 +54,26 @@
     // 2 events in bucket 1.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
-    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+
+    // Flushes at event #2.
+    countProducer.flushIfNeeded(bucketStartTimeNs + 2);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    // Flushes.
+    countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
     const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets.size());
-    const auto& bucketInfo = buckets[0];
-    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
-    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
-    EXPECT_EQ(2LL, bucketInfo.mCount);
+    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(2LL, buckets[0].mCount);
 
     // 1 matched event happens in bucket 2.
     LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
-    countProducer.flushCounterIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    countProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
@@ -79,7 +84,7 @@
     EXPECT_EQ(1LL, bucketInfo2.mCount);
 
     // nothing happens in bucket 3. we should not record anything for bucket 3.
-    countProducer.flushCounterIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    countProducer.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
@@ -108,20 +113,22 @@
     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
 
     countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+    // Upon this match event, the matched event1 is flushed.
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
 
-    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-
+    countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
-    EXPECT_EQ(1UL, buckets.size());
-    const auto& bucketInfo = buckets[0];
-    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
-    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
-    EXPECT_EQ(1LL, bucketInfo.mCount);
+    {
+        const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+        EXPECT_EQ(1UL, buckets.size());
+        const auto& bucketInfo = buckets[0];
+        EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+        EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+        EXPECT_EQ(1LL, bucketInfo.mCount);
+    }
 }
 
 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
@@ -158,10 +165,11 @@
                                       bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+    countProducer.flushIfNeeded(bucketStartTimeNs + 1);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
-
-    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-
+    countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
@@ -173,6 +181,68 @@
     EXPECT_EQ(1LL, bucketInfo.mCount);
 }
 
+TEST(CountMetricProducerTest, TestAnomalyDetection) {
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name("1");
+    alert.set_trigger_if_sum_gt(2);
+    alert.set_number_of_buckets(2);
+    alert.set_refractory_period_secs(1);
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 30 * NS_PER_SEC;
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+
+    CountMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+                                      bucketStartTimeNs);
+    countProducer.addAnomalyTracker(anomalyTracker);
+
+    int tagId = 1;
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
+    LogEvent event2(tagId, bucketStartTimeNs + 2);
+    LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
+    LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
+    LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3 + NS_PER_SEC);
+
+    // Two events in bucket #0.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+    // One event in bucket #2. No alarm as bucket #0 is trashed out.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+    // Two events in bucket #3.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4, false);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5, false);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6, false);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
+    // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event5.GetTimestampNs());
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7, false);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event7.GetTimestampNs());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
new file mode 100644
index 0000000..b9e2b8a
--- /dev/null
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -0,0 +1,201 @@
+// 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.
+
+#include "logd/LogEvent.h"
+#include "metrics_test_helper.h"
+#include "src/metrics/GaugeMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(GaugeMetricProducerTest, TestWithCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+    GaugeMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_gauge_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    GaugeMetricProducer gaugeProducer(metric, 1 /*has condition*/, wizard, -1, bucketStartTimeNs);
+
+    vector<std::shared_ptr<LogEvent>> allData;
+    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+    event1->write(1);
+    event1->write(13);
+    event1->init();
+    allData.push_back(event1);
+
+    std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+    allData.push_back(event2);
+
+    gaugeProducer.onDataPulled(allData);
+    gaugeProducer.flushIfNeeded(event2->GetTimestampNs() + 1);
+    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+    gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 11);
+    gaugeProducer.onConditionChanged(false, bucketStartTimeNs + 21);
+    gaugeProducer.onConditionChanged(true, bucketStartTimeNs + bucketSizeNs + 11);
+    std::shared_ptr<LogEvent> event3 =
+            std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+    event3->write(1);
+    event3->write(25);
+    event3->init();
+    allData.push_back(event3);
+    gaugeProducer.onDataPulled(allData);
+    gaugeProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 10);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+    // One dimension.
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              gaugeProducer.mPastBuckets.begin()->second.front().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestNoCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+    GaugeMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_gauge_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+    vector<std::shared_ptr<LogEvent>> allData;
+    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+    event1->write(1);
+    event1->write(13);
+    event1->init();
+    allData.push_back(event1);
+
+    std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+    allData.push_back(event2);
+
+    std::shared_ptr<LogEvent> event3 =
+            std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+    event3->write(1);
+    event3->write(25);
+    event3->init();
+    allData.push_back(event3);
+
+    gaugeProducer.onDataPulled(allData);
+    // Has one slice
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(13L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+    EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mGauge);
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              gaugeProducer.mPastBuckets.begin()->second.back().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    GaugeMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_gauge_field(2);
+    GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name("1");
+    alert.set_trigger_if_sum_gt(25);
+    alert.set_number_of_buckets(2);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+    gaugeProducer.addAnomalyTracker(anomalyTracker);
+
+    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+    event1->write(1);
+    event1->write(13);
+    event1->init();
+
+    gaugeProducer.onDataPulled({event1});
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+    std::shared_ptr<LogEvent> event2 =
+            std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+
+    gaugeProducer.onDataPulled({event2});
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event2->GetTimestampNs());
+
+    std::shared_ptr<LogEvent> event3 =
+            std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+    event3->write(1);
+    event3->write(24);
+    event3->init();
+
+    gaugeProducer.onDataPulled({event3});
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+
+    // The event4 does not have the gauge field. Thus the current bucket value is 0.
+    std::shared_ptr<LogEvent> event4 =
+            std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10);
+    event4->write(1);
+    event4->init();
+    gaugeProducer.onDataPulled({event4});
+    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 9cc184a..9e169bb 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,17 +45,49 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+                               buckets);
 
-    tracker.noteStart("", true, bucketStartTimeNs, key1);
-    tracker.noteStop("", bucketStartTimeNs + 10, false);
+    tracker.noteStart("1", true, bucketStartTimeNs, key1);
+    // Event starts again. This would not change anything as it already starts.
+    tracker.noteStart("1", true, bucketStartTimeNs + 3, key1);
+    // Stopped.
+    tracker.noteStop("1", bucketStartTimeNs + 10, false);
 
-    tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
-    tracker.noteStop("", bucketStartTimeNs + 40, false);
+    // Another event starts in this bucket.
+    tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+    tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(20, buckets[0].mDuration);
+    EXPECT_EQ(20ULL, buckets[0].mDuration);
+}
+
+TEST(MaxDurationTrackerTest, TestStopAll) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    vector<DurationBucket> buckets;
+    ConditionKey key1;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+                               buckets);
+
+    tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
+
+    // Another event starts in this bucket.
+    tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+    tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
+    EXPECT_TRUE(tracker.mInfos.empty());
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(40ULL, buckets[1].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
@@ -67,14 +99,33 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+                               buckets);
 
+    // The event starts.
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
-    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
 
+    // Starts again. Does not change anything.
+    tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
+
+    // Flushes at early 2nd bucket. The event still does not stop yet.
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+
+    // Flushes at the end of the 2nd bucket. The event still does not stop yet.
+    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs));
     EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
-    EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+
+    // The event stops at early 4th bucket.
+    tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 21);
+    EXPECT_EQ(3u, buckets.size());
+    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
@@ -86,7 +137,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
+                               buckets);
 
     // 2 starts
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -97,15 +149,17 @@
     tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
 
     EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
-    EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
 
     // real stop now.
     tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
 
     EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ(5, buckets[2].mDuration);
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ(5ULL, buckets[2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
@@ -124,17 +178,65 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+                               buckets);
+    EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
 
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_TRUE(tracker.mInfos.empty());
+    EXPECT_EQ(6LL, tracker.mDuration);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+    tracker.noteStart("2:maps", false, eventStartTimeNs + 3 * bucketSizeNs + 10, key1);
+    EXPECT_EQ(1u, tracker.mInfos.size());
+    for (const auto& itr : tracker.mInfos) {
+        EXPECT_EQ(DurationState::kPaused, itr.second.state);
+        EXPECT_EQ(0LL, itr.second.lastDuration);
+    }
+    EXPECT_EQ(3u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ(6ULL, buckets[2].mDuration);
+}
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5, buckets[0].mDuration);
+TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name("1");
+    alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
+    alert.set_number_of_buckets(2);
+    alert.set_refractory_period_secs(1);
+
+    vector<DurationBucket> buckets;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+    uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+    uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
+                               {anomalyTracker}, buckets);
+
+    tracker.noteStart("1", true, eventStartTimeNs, key1);
+    tracker.noteStop("1", eventStartTimeNs + 10, false);
+    EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+    EXPECT_EQ(10LL, tracker.mDuration);
+
+    tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
+    tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
+    EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
+    EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
+              (long long)(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC));
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index f495d6b..f4edffd 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -45,16 +45,18 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-    int64_t durationTimeNs = 2 * 1000;
+    uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+    EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
-
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
 }
@@ -71,7 +73,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -81,7 +84,66 @@
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(2003, buckets[0].mDuration);
+    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestStopAll) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+
+    tracker.noteStopAll(eventStartTimeNs + 2003);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    uint64_t durationTimeNs = 2 * 1000;
+
+    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+    tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
+    EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
+
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
+    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
@@ -98,19 +160,19 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-    int64_t durationTimeNs = 2 * 1000;
+    uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
-
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
-
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5, buckets[0].mDuration);
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+    tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + durationTimeNs);
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
@@ -128,7 +190,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+                                 buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
@@ -141,7 +204,105 @@
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(15, buckets[0].mDuration);
+    EXPECT_EQ(15ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name("1");
+    alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+    alert.set_number_of_buckets(2);
+    alert.set_refractory_period_secs(1);
+
+    vector<DurationBucket> buckets;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+    uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+    uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
+                                 {anomalyTracker}, buckets);
+
+    // Nothing in the past bucket.
+    tracker.noteStart("", true, eventStartTimeNs, key1);
+    EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+
+    tracker.noteStop("", eventStartTimeNs + 3, false);
+    EXPECT_EQ(0u, buckets.size());
+
+    uint64_t event1StartTimeNs = eventStartTimeNs + 10;
+    tracker.noteStart("1", true, event1StartTimeNs, key1);
+    // No past buckets. The anomaly will happen in bucket #0.
+    EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
+
+    uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
+    tracker.noteStop("1", event1StopTimeNs, false);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
+              buckets[0].mDuration);
+
+    const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
+    const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
+
+    // One past buckets. The anomaly will happen in bucket #1.
+    uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
+    tracker.noteStart("1", true, event2StartTimeNs, key1);
+    EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
+                          bucket1Duration),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
+    tracker.noteStop("1", event2StartTimeNs + 1, false);
+
+    // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
+    // bucket #2.
+    uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
+    tracker.noteStart("1", true, event3StartTimeNs, key1);
+    EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
+}
+
+TEST(OringDurationTrackerTest, TestAnomalyDetection) {
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name("1");
+    alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+    alert.set_number_of_buckets(2);
+    alert.set_refractory_period_secs(1);
+
+    vector<DurationBucket> buckets;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+    uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+    uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+    OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
+                                 bucketSizeNs, {anomalyTracker}, buckets);
+
+    tracker.noteStart("", true, eventStartTimeNs, key1);
+    tracker.noteStop("", eventStartTimeNs + 10, false);
+    EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+    EXPECT_TRUE(tracker.mStarted.empty());
+    EXPECT_EQ(-1LL, tracker.mLastStartTime);
+    EXPECT_EQ(10LL, tracker.mDuration);
+
+    EXPECT_EQ(0u, tracker.mStarted.size());
+
+    tracker.noteStart("", true, eventStartTimeNs + 20, key1);
+    EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+    EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
+              (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
+    tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
+    EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
+    EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
+              anomalyTracker->mLastAlarmTimestampNs);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index cd647cb..1ed3636 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -277,7 +277,7 @@
     EXPECT_EQ(20, curInterval.raw.back().first);
     EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
 
-    valueProducer.flush_if_needed(bucket3StartTimeNs);
+    valueProducer.flushIfNeeded(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
     EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
index 66e762c..955e58e 100644
--- a/cmds/webview_zygote/Android.mk
+++ b/cmds/webview_zygote/Android.mk
@@ -21,6 +21,8 @@
 
 LOCAL_SRC_FILES := webview_zygote.cpp
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_SHARED_LIBRARIES := \
 	libandroid_runtime \
 	libbinder \
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index e491a4f..da5569d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -359,6 +359,8 @@
             if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
                     + ", views=" + mNumReadViews);
+            mCurParcel.recycle();
+            mCurParcel = null; // Parcel cannot be used after recycled.
         }
 
         Parcel readParcel(int validateToken, int level) {
@@ -396,20 +398,23 @@
 
         private void fetchData() {
             Parcel data = Parcel.obtain();
-            data.writeInterfaceToken(DESCRIPTOR);
-            data.writeStrongBinder(mTransferToken);
-            if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
-            if (mCurParcel != null) {
-                mCurParcel.recycle();
-            }
-            mCurParcel = Parcel.obtain();
             try {
-                mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failure reading AssistStructure data", e);
-                throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+                data.writeInterfaceToken(DESCRIPTOR);
+                data.writeStrongBinder(mTransferToken);
+                if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
+                if (mCurParcel != null) {
+                    mCurParcel.recycle();
+                }
+                mCurParcel = Parcel.obtain();
+                try {
+                    mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failure reading AssistStructure data", e);
+                    throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+                }
+            } finally {
+                data.recycle();
             }
-            data.recycle();
             mNumReadWindows = mNumReadViews = 0;
         }
     }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 616a5be..c5f2272 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -33,7 +33,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.widget.RemoteViews;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
@@ -41,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A slice is a piece of app content and actions that can be surfaced outside of the app.
@@ -54,7 +54,7 @@
      * @hide
      */
     @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
-            HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL})
+            HINT_NO_TINT, HINT_PARTIAL})
     public @interface SliceHint{ }
 
     /**
@@ -62,8 +62,7 @@
      * the content should be used in the shortcut representation of the slice (icon, label, action),
      * normally this should be indicated by adding the hint on the action containing that content.
      *
-     * @see SliceView#MODE_SHORTCUT
-     * @see SliceItem#TYPE_ACTION
+     * @see SliceItem#FORMAT_ACTION
      */
     public static final String HINT_TITLE       = "title";
     /**
@@ -91,27 +90,13 @@
      */
     public static final String HINT_SELECTED    = "selected";
     /**
-     * Hint to indicate that this is a message as part of a communication
-     * sequence in this slice.
-     */
-    public static final String HINT_MESSAGE     = "message";
-    /**
-     * Hint to tag the source (i.e. sender) of a {@link #HINT_MESSAGE}.
-     */
-    public static final String HINT_SOURCE      = "source";
-    /**
-     * Hint that list items within this slice or subslice would appear better
-     * if organized horizontally.
-     */
-    public static final String HINT_HORIZONTAL  = "horizontal";
-    /**
      * Hint to indicate that this content should not be tinted.
      */
     public static final String HINT_NO_TINT     = "no_tint";
     /**
-     * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
-     * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
-     * the {@link SliceView#MODE_SHORTCUT} format of the slice.
+     * Hint to indicate that this content should not be shown in larger renderings
+     * of Slices. This content may be used to populate the shortcut/icon
+     * format of the slice.
      * @hide
      */
     public static final String HINT_HIDDEN = "hidden";
@@ -124,32 +109,42 @@
      */
     public static final String HINT_TOGGLE = "toggle";
     /**
+     * Hint that list items within this slice or subslice would appear better
+     * if organized horizontally.
+     */
+    public static final String HINT_HORIZONTAL = "horizontal";
+    /**
      * Hint to indicate that this slice is incomplete and an update will be sent once
      * loading is complete. Slices which contain HINT_PARTIAL will not be cached by the
      * OS and should not be cached by apps.
      */
     public static final String HINT_PARTIAL     = "partial";
 
-    // These two are coming over from prototyping, but we probably don't want in
-    // public API, at least not right now.
-    /**
-     * @hide
-     */
-    public static final String HINT_ALT         = "alt";
     /**
      * Key to retrieve an extra added to an intent when a control is changed.
      * @hide
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
+    /**
+     * Subtype to indicate that this is a message as part of a communication
+     * sequence in this slice.
+     */
+    public static final String SUBTYPE_MESSAGE = "message";
+    /**
+     * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}.
+     */
+    public static final String SUBTYPE_SOURCE = "source";
 
     private final SliceItem[] mItems;
     private final @SliceHint String[] mHints;
+    private SliceSpec mSpec;
     private Uri mUri;
 
-    Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+    Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri, SliceSpec spec) {
         mHints = hints;
         mItems = items.toArray(new SliceItem[items.size()]);
         mUri = uri;
+        mSpec = spec;
     }
 
     protected Slice(Parcel in) {
@@ -160,6 +155,14 @@
             mItems[i] = SliceItem.CREATOR.createFromParcel(in);
         }
         mUri = Uri.CREATOR.createFromParcel(in);
+        mSpec = in.readTypedObject(SliceSpec.CREATOR);
+    }
+
+    /**
+     * @return The spec for this slice
+     */
+    public @Nullable SliceSpec getSpec() {
+        return mSpec;
     }
 
     /**
@@ -191,6 +194,7 @@
             mItems[i].writeToParcel(dest, flags);
         }
         mUri.writeToParcel(dest, 0);
+        dest.writeTypedObject(mSpec, flags);
     }
 
     @Override
@@ -213,6 +217,7 @@
         private final Uri mUri;
         private ArrayList<SliceItem> mItems = new ArrayList<>();
         private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+        private SliceSpec mSpec;
 
         /**
          * Create a builder which will construct a {@link Slice} for the Given Uri.
@@ -248,11 +253,28 @@
         }
 
         /**
+         * Add the spec for this slice.
+         */
+        public Builder setSpec(SliceSpec spec) {
+            mSpec = spec;
+            return this;
+        }
+
+        /**
          * Add a sub-slice to the slice being constructed
          */
         public Builder addSubSlice(@NonNull Slice slice) {
-            mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray(
-                    new String[slice.getHints().size()])));
+            return addSubSlice(slice, null);
+        }
+
+        /**
+         * Add a sub-slice to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Builder addSubSlice(@NonNull Slice slice, @Nullable String subType) {
+            mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
+                    slice.getHints().toArray(new String[slice.getHints().size()])));
             return this;
         }
 
@@ -260,99 +282,132 @@
          * Add an action to the slice being constructed
          */
         public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
-            mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0]));
+            return addAction(action, s, null);
+        }
+
+        /**
+         * Add an action to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
+                @Nullable String subType) {
+            List<String> hints = s.getHints();
+            s.mSpec = null;
+            mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
+                    new String[hints.size()])));
             return this;
         }
 
         /**
          * Add text to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Builder addText(CharSequence text, @SliceHint String... hints) {
-            mItems.add(new SliceItem(text, SliceItem.TYPE_TEXT, hints));
+        public Builder addText(CharSequence text, @Nullable String subType,
+                @SliceHint String... hints) {
+            mItems.add(new SliceItem(text, SliceItem.FORMAT_TEXT, subType, hints));
             return this;
         }
 
         /**
          * Add text to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Builder addText(CharSequence text, @SliceHint List<String> hints) {
-            return addText(text, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add an image to the slice being constructed
-         */
-        public Builder addIcon(Icon icon, @SliceHint String... hints) {
-            mItems.add(new SliceItem(icon, SliceItem.TYPE_IMAGE, hints));
-            return this;
-        }
-
-        /**
-         * Add an image to the slice being constructed
-         */
-        public Builder addIcon(Icon icon, @SliceHint List<String> hints) {
-            return addIcon(icon, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * @hide This isn't final
-         */
-        public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) {
-            mItems.add(new SliceItem(remoteView, SliceItem.TYPE_REMOTE_VIEW, hints));
-            return this;
-        }
-
-        /**
-         * Add remote input to the slice being constructed
-         */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput,
+        public Builder addText(CharSequence text, @Nullable String subType,
                 @SliceHint List<String> hints) {
-            return addRemoteInput(remoteInput, hints.toArray(new String[hints.size()]));
+            return addText(text, subType, hints.toArray(new String[hints.size()]));
+        }
+
+        /**
+         * Add an image to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint String... hints) {
+            mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
+            return this;
+        }
+
+        /**
+         * Add an image to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint List<String> hints) {
+            return addIcon(icon, subType, hints.toArray(new String[hints.size()]));
         }
 
         /**
          * Add remote input to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) {
-            mItems.add(new SliceItem(remoteInput, SliceItem.TYPE_REMOTE_INPUT, hints));
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+                @SliceHint List<String> hints) {
+            return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()]));
+        }
+
+        /**
+         * Add remote input to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+                @SliceHint String... hints) {
+            mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
+                    subType, hints));
             return this;
         }
 
         /**
          * Add a color to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Builder addColor(int color, @SliceHint String... hints) {
-            mItems.add(new SliceItem(color, SliceItem.TYPE_COLOR, hints));
+        public Builder addColor(int color, @Nullable String subType, @SliceHint String... hints) {
+            mItems.add(new SliceItem(color, SliceItem.FORMAT_COLOR, subType, hints));
             return this;
         }
 
         /**
          * Add a color to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Builder addColor(int color, @SliceHint List<String> hints) {
-            return addColor(color, hints.toArray(new String[hints.size()]));
+        public Builder addColor(int color, @Nullable String subType,
+                @SliceHint List<String> hints) {
+            return addColor(color, subType, hints.toArray(new String[hints.size()]));
         }
 
         /**
          * Add a timestamp to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addTimestamp(long time, @SliceHint String... hints) {
-            mItems.add(new SliceItem(time, SliceItem.TYPE_TIMESTAMP, hints));
+        public Slice.Builder addTimestamp(long time, @Nullable String subType,
+                @SliceHint String... hints) {
+            mItems.add(new SliceItem(time, SliceItem.FORMAT_TIMESTAMP, subType,
+                    hints));
             return this;
         }
 
         /**
          * Add a timestamp to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addTimestamp(long time, @SliceHint List<String> hints) {
-            return addTimestamp(time, hints.toArray(new String[hints.size()]));
+        public Slice.Builder addTimestamp(long time, @Nullable String subType,
+                @SliceHint List<String> hints) {
+            return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
         }
 
         /**
          * Construct the slice.
          */
         public Slice build() {
-            return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+            return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri, mSpec);
         }
     }
 
@@ -380,15 +435,15 @@
         StringBuilder sb = new StringBuilder();
         for (int i = 0; i < mItems.length; i++) {
             sb.append(indent);
-            if (mItems[i].getType() == SliceItem.TYPE_SLICE) {
+            if (Objects.equals(mItems[i].getFormat(), SliceItem.FORMAT_SLICE)) {
                 sb.append("slice:\n");
                 sb.append(mItems[i].getSlice().toString(indent + "   "));
-            } else if (mItems[i].getType() == SliceItem.TYPE_TEXT) {
+            } else if (Objects.equals(mItems[i].getFormat(), SliceItem.FORMAT_TEXT)) {
                 sb.append("text: ");
                 sb.append(mItems[i].getText());
                 sb.append("\n");
             } else {
-                sb.append(SliceItem.typeToString(mItems[i].getType()));
+                sb.append(mItems[i].getFormat());
                 sb.append("\n");
             }
         }
@@ -400,10 +455,12 @@
      *
      * @param resolver ContentResolver to be used.
      * @param uri The URI to a slice provider
+     * @param supportedSpecs List of supported specs.
      * @return The Slice provided by the app or null if none is given.
      * @see Slice
      */
-    public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) {
+    public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
+            List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         IContentProvider provider = resolver.acquireProvider(uri);
         if (provider == null) {
@@ -412,6 +469,8 @@
         try {
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
                     null, extras);
             Bundle.setDefusable(res, true);
@@ -435,12 +494,14 @@
      *
      * @param context The context to use.
      * @param intent The intent associated with a slice.
+     * @param supportedSpecs List of supported specs.
      * @return The Slice provided by the app or null if none is given.
      * @see Slice
      * @see SliceProvider#onMapIntentToUri(Intent)
      * @see Intent
      */
-    public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) {
+    public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
+            List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
                 "Slice intent must be explicit " + intent);
@@ -449,7 +510,7 @@
         // Check if the intent has data for the slice uri on it and use that
         final Uri intentData = intent.getData();
         if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return bindSlice(resolver, intentData);
+            return bindSlice(resolver, intentData, supportedSpecs);
         }
         // Otherwise ask the app
         List<ResolveInfo> providers =
@@ -467,6 +528,8 @@
         try {
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(resolver.getPackageName(),
                     SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 6e69b05..8d81199 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -16,8 +16,8 @@
 
 package android.app.slice;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.StringDef;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.graphics.drawable.Icon;
@@ -38,13 +38,13 @@
  *
  * A SliceItem a piece of content and some hints about what that content
  * means or how it should be displayed. The types of content can be:
- * <li>{@link #TYPE_SLICE}</li>
- * <li>{@link #TYPE_TEXT}</li>
- * <li>{@link #TYPE_IMAGE}</li>
- * <li>{@link #TYPE_ACTION}</li>
- * <li>{@link #TYPE_COLOR}</li>
- * <li>{@link #TYPE_TIMESTAMP}</li>
- * <li>{@link #TYPE_REMOTE_INPUT}</li>
+ * <li>{@link #FORMAT_SLICE}</li>
+ * <li>{@link #FORMAT_TEXT}</li>
+ * <li>{@link #FORMAT_IMAGE}</li>
+ * <li>{@link #FORMAT_ACTION}</li>
+ * <li>{@link #FORMAT_COLOR}</li>
+ * <li>{@link #FORMAT_TIMESTAMP}</li>
+ * <li>{@link #FORMAT_REMOTE_INPUT}</li>
  *
  * The hints that a {@link SliceItem} are a set of strings which annotate
  * the content. The hints that are guaranteed to be understood by the system
@@ -55,68 +55,68 @@
     /**
      * @hide
      */
-    @IntDef({TYPE_SLICE, TYPE_TEXT, TYPE_IMAGE, TYPE_ACTION, TYPE_COLOR,
-            TYPE_TIMESTAMP, TYPE_REMOTE_INPUT})
+    @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_COLOR,
+            FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
     public @interface SliceType {}
 
     /**
      * A {@link SliceItem} that contains a {@link Slice}
      */
-    public static final int TYPE_SLICE        = 1;
+    public static final String FORMAT_SLICE = "slice";
     /**
      * A {@link SliceItem} that contains a {@link CharSequence}
      */
-    public static final int TYPE_TEXT         = 2;
+    public static final String FORMAT_TEXT = "text";
     /**
      * A {@link SliceItem} that contains an {@link Icon}
      */
-    public static final int TYPE_IMAGE        = 3;
+    public static final String FORMAT_IMAGE = "image";
     /**
      * A {@link SliceItem} that contains a {@link PendingIntent}
      *
      * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
      * item contains a {@link Slice} that the action applies to.
      */
-    public static final int TYPE_ACTION       = 4;
-    /**
-     * @hide This isn't final
-     */
-    public static final int TYPE_REMOTE_VIEW  = 5;
+    public static final String FORMAT_ACTION = "action";
     /**
      * A {@link SliceItem} that contains a Color int.
      */
-    public static final int TYPE_COLOR        = 6;
+    public static final String FORMAT_COLOR = "color";
     /**
      * A {@link SliceItem} that contains a timestamp.
      */
-    public static final int TYPE_TIMESTAMP    = 8;
+    public static final String FORMAT_TIMESTAMP = "timestamp";
     /**
      * A {@link SliceItem} that contains a {@link RemoteInput}.
      */
-    public static final int TYPE_REMOTE_INPUT = 9;
+    public static final String FORMAT_REMOTE_INPUT = "input";
 
     /**
      * @hide
      */
     protected @Slice.SliceHint
     String[] mHints;
-    private final int mType;
+    private final String mFormat;
+    private final String mSubType;
     private final Object mObj;
 
     /**
      * @hide
      */
-    public SliceItem(Object obj, @SliceType int type, @Slice.SliceHint String[] hints) {
+    public SliceItem(Object obj, @SliceType String format, String subType,
+            @Slice.SliceHint String[] hints) {
         mHints = hints;
-        mType = type;
+        mFormat = format;
+        mSubType = subType;
         mObj = obj;
     }
 
     /**
      * @hide
      */
-    public SliceItem(PendingIntent intent, Slice slice, int type, @Slice.SliceHint String[] hints) {
-        this(new Pair<>(intent, slice), type, hints);
+    public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
+            @Slice.SliceHint String[] hints) {
+        this(new Pair<>(intent, slice), format, subType, hints);
     }
 
     /**
@@ -141,26 +141,51 @@
         ArrayUtils.removeElement(String.class, mHints, hint);
     }
 
-    public @SliceType int getType() {
-        return mType;
+    /**
+     * Get the format of this SliceItem.
+     * <p>
+     * The format will be one of the following types supported by the platform:
+     * <li>{@link #FORMAT_SLICE}</li>
+     * <li>{@link #FORMAT_TEXT}</li>
+     * <li>{@link #FORMAT_IMAGE}</li>
+     * <li>{@link #FORMAT_ACTION}</li>
+     * <li>{@link #FORMAT_COLOR}</li>
+     * <li>{@link #FORMAT_TIMESTAMP}</li>
+     * <li>{@link #FORMAT_REMOTE_INPUT}</li>
+     * @see #getSubType() ()
+     */
+    public String getFormat() {
+        return mFormat;
     }
 
     /**
-     * @return The text held by this {@link #TYPE_TEXT} SliceItem
+     * Get the sub-type of this SliceItem.
+     * <p>
+     * Subtypes provide additional information about the type of this information beyond basic
+     * interpretations inferred by {@link #getFormat()}. For example a slice may contain
+     * many {@link #FORMAT_TEXT} items, but only some of them may be {@link Slice#SUBTYPE_MESSAGE}.
+     * @see #getFormat()
+     */
+    public String getSubType() {
+        return mSubType;
+    }
+
+    /**
+     * @return The text held by this {@link #FORMAT_TEXT} SliceItem
      */
     public CharSequence getText() {
         return (CharSequence) mObj;
     }
 
     /**
-     * @return The icon held by this {@link #TYPE_IMAGE} SliceItem
+     * @return The icon held by this {@link #FORMAT_IMAGE} SliceItem
      */
     public Icon getIcon() {
         return (Icon) mObj;
     }
 
     /**
-     * @return The pending intent held by this {@link #TYPE_ACTION} SliceItem
+     * @return The pending intent held by this {@link #FORMAT_ACTION} SliceItem
      */
     public PendingIntent getAction() {
         return ((Pair<PendingIntent, Slice>) mObj).first;
@@ -174,31 +199,31 @@
     }
 
     /**
-     * @return The remote input held by this {@link #TYPE_REMOTE_INPUT} SliceItem
+     * @return The remote input held by this {@link #FORMAT_REMOTE_INPUT} SliceItem
      */
     public RemoteInput getRemoteInput() {
         return (RemoteInput) mObj;
     }
 
     /**
-     * @return The color held by this {@link #TYPE_COLOR} SliceItem
+     * @return The color held by this {@link #FORMAT_COLOR} SliceItem
      */
     public int getColor() {
         return (Integer) mObj;
     }
 
     /**
-     * @return The slice held by this {@link #TYPE_ACTION} or {@link #TYPE_SLICE} SliceItem
+     * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
      */
     public Slice getSlice() {
-        if (getType() == TYPE_ACTION) {
+        if (getFormat() == FORMAT_ACTION) {
             return ((Pair<PendingIntent, Slice>) mObj).second;
         }
         return (Slice) mObj;
     }
 
     /**
-     * @return The timestamp held by this {@link #TYPE_TIMESTAMP} SliceItem
+     * @return The timestamp held by this {@link #FORMAT_TIMESTAMP} SliceItem
      */
     public long getTimestamp() {
         return (Long) mObj;
@@ -217,8 +242,9 @@
      */
     public SliceItem(Parcel in) {
         mHints = in.readStringArray();
-        mType = in.readInt();
-        mObj = readObj(mType, in);
+        mFormat = in.readString();
+        mSubType = in.readString();
+        mObj = readObj(mFormat, in);
     }
 
     @Override
@@ -229,8 +255,9 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeStringArray(mHints);
-        dest.writeInt(mType);
-        writeObj(dest, flags, mObj, mType);
+        dest.writeString(mFormat);
+        dest.writeString(mSubType);
+        writeObj(dest, flags, mObj, mFormat);
     }
 
     /**
@@ -259,49 +286,54 @@
         return false;
     }
 
-    private void writeObj(Parcel dest, int flags, Object obj, int type) {
-        switch (type) {
-            case TYPE_SLICE:
-            case TYPE_REMOTE_VIEW:
-            case TYPE_IMAGE:
-            case TYPE_REMOTE_INPUT:
+    private static String getBaseType(String type) {
+        int index = type.indexOf('/');
+        if (index >= 0) {
+            return type.substring(0, index);
+        }
+        return type;
+    }
+
+    private static void writeObj(Parcel dest, int flags, Object obj, String type) {
+        switch (getBaseType(type)) {
+            case FORMAT_SLICE:
+            case FORMAT_IMAGE:
+            case FORMAT_REMOTE_INPUT:
                 ((Parcelable) obj).writeToParcel(dest, flags);
                 break;
-            case TYPE_ACTION:
+            case FORMAT_ACTION:
                 ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
                 ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
                 break;
-            case TYPE_TEXT:
-                TextUtils.writeToParcel((CharSequence) mObj, dest, flags);
+            case FORMAT_TEXT:
+                TextUtils.writeToParcel((CharSequence) obj, dest, flags);
                 break;
-            case TYPE_COLOR:
-                dest.writeInt((Integer) mObj);
+            case FORMAT_COLOR:
+                dest.writeInt((Integer) obj);
                 break;
-            case TYPE_TIMESTAMP:
-                dest.writeLong((Long) mObj);
+            case FORMAT_TIMESTAMP:
+                dest.writeLong((Long) obj);
                 break;
         }
     }
 
-    private static Object readObj(int type, Parcel in) {
-        switch (type) {
-            case TYPE_SLICE:
+    private static Object readObj(String type, Parcel in) {
+        switch (getBaseType(type)) {
+            case FORMAT_SLICE:
                 return Slice.CREATOR.createFromParcel(in);
-            case TYPE_TEXT:
+            case FORMAT_TEXT:
                 return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-            case TYPE_IMAGE:
+            case FORMAT_IMAGE:
                 return Icon.CREATOR.createFromParcel(in);
-            case TYPE_ACTION:
-                return new Pair<PendingIntent, Slice>(
+            case FORMAT_ACTION:
+                return new Pair<>(
                         PendingIntent.CREATOR.createFromParcel(in),
                         Slice.CREATOR.createFromParcel(in));
-            case TYPE_REMOTE_VIEW:
-                return RemoteViews.CREATOR.createFromParcel(in);
-            case TYPE_COLOR:
+            case FORMAT_COLOR:
                 return in.readInt();
-            case TYPE_TIMESTAMP:
+            case FORMAT_TIMESTAMP:
                 return in.readLong();
-            case TYPE_REMOTE_INPUT:
+            case FORMAT_REMOTE_INPUT:
                 return RemoteInput.CREATOR.createFromParcel(in);
         }
         throw new RuntimeException("Unsupported type " + type);
@@ -318,29 +350,4 @@
             return new SliceItem[size];
         }
     };
-
-    /**
-     * @hide
-     */
-    public static String typeToString(int type) {
-        switch (type) {
-            case TYPE_SLICE:
-                return "Slice";
-            case TYPE_TEXT:
-                return "Text";
-            case TYPE_IMAGE:
-                return "Image";
-            case TYPE_ACTION:
-                return "Action";
-            case TYPE_REMOTE_VIEW:
-                return "RemoteView";
-            case TYPE_COLOR:
-                return "Color";
-            case TYPE_TIMESTAMP:
-                return "Timestamp";
-            case TYPE_REMOTE_INPUT:
-                return "RemoteInput";
-        }
-        return "Unrecognized type: " + type;
-    }
 }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 05f4ce6..ac5365c 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -17,7 +17,6 @@
 
 import android.Manifest.permission;
 import android.annotation.NonNull;
-import android.app.slice.widget.SliceView;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -37,6 +36,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -93,6 +93,10 @@
     /**
      * @hide
      */
+    public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
+    /**
+     * @hide
+     */
     public static final String METHOD_SLICE = "bind_slice";
     /**
      * @hide
@@ -118,12 +122,25 @@
      * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
      * when the app is ready to provide the complete data in onBindSlice.
      * <p>
+     * The slice returned should have a spec that is compatible with one of
+     * the supported specs.
      *
+     * @param sliceUri Uri to bind.
+     * @param supportedSpecs List of supported specs.
      * @see {@link Slice}.
      * @see {@link Slice#HINT_PARTIAL}
      */
-    // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
-    public abstract Slice onBindSlice(Uri sliceUri);
+    public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+        return onBindSlice(sliceUri);
+    }
+
+    /**
+     * @deprecated migrating to {@link #onBindSlice(Uri, List)}
+     */
+    @Deprecated
+    public Slice onBindSlice(Uri sliceUri) {
+        return null;
+    }
 
     /**
      * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
@@ -132,7 +149,6 @@
      *
      * @return Uri representing the slice associated with the provided intent.
      * @see {@link Slice}
-     * @see {@link SliceView#setSlice(Intent)}
      */
     public @NonNull Uri onMapIntentToUri(Intent intent) {
         throw new UnsupportedOperationException(
@@ -195,8 +211,9 @@
                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
                         "Slice binding requires the permission BIND_SLICE");
             }
+            List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
-            Slice s = handleBindSlice(uri);
+            Slice s = handleBindSlice(uri, supportedSpecs);
             Bundle b = new Bundle();
             b.putParcelable(EXTRA_SLICE, s);
             return b;
@@ -205,9 +222,10 @@
                     "Slice binding requires the permission BIND_SLICE");
             Intent intent = extras.getParcelable(EXTRA_INTENT);
             Uri uri = onMapIntentToUri(intent);
+            List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
             Bundle b = new Bundle();
             if (uri != null) {
-                Slice s = handleBindSlice(uri);
+                Slice s = handleBindSlice(uri, supportedSpecs);
                 b.putParcelable(EXTRA_SLICE, s);
             } else {
                 b.putParcelable(EXTRA_SLICE, null);
@@ -217,14 +235,14 @@
         return super.call(method, arg, extras);
     }
 
-    private Slice handleBindSlice(Uri sliceUri) {
+    private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         if (Looper.myLooper() == Looper.getMainLooper()) {
-            return onBindSliceStrict(sliceUri);
+            return onBindSliceStrict(sliceUri, supportedSpecs);
         } else {
             CountDownLatch latch = new CountDownLatch(1);
             Slice[] output = new Slice[1];
             Handler.getMain().post(() -> {
-                output[0] = onBindSliceStrict(sliceUri);
+                output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
                 latch.countDown();
             });
             try {
@@ -236,14 +254,14 @@
         }
     }
 
-    private Slice onBindSliceStrict(Uri sliceUri) {
+    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectAll()
                     .penaltyDeath()
                     .build());
-            return onBindSlice(sliceUri);
+            return onBindSlice(sliceUri, supportedSpecs);
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
diff --git a/core/java/android/app/slice/SliceQuery.java b/core/java/android/app/slice/SliceQuery.java
index 9943c49..20eca88 100644
--- a/core/java/android/app/slice/SliceQuery.java
+++ b/core/java/android/app/slice/SliceQuery.java
@@ -19,6 +19,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.Spliterators;
 import java.util.stream.Collectors;
@@ -37,14 +38,15 @@
      */
     public static SliceItem getPrimaryIcon(Slice slice) {
         for (SliceItem item : slice.getItems()) {
-            if (item.getType() == SliceItem.TYPE_IMAGE) {
+            if (Objects.equals(item.getFormat(), SliceItem.FORMAT_IMAGE)) {
                 return item;
             }
-            if (!(item.getType() == SliceItem.TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
+            if (!(compareTypes(item, SliceItem.FORMAT_SLICE)
+                    && item.hasHint(Slice.HINT_LIST))
                     && !item.hasHint(Slice.HINT_ACTIONS)
                     && !item.hasHint(Slice.HINT_LIST_ITEM)
-                    && (item.getType() != SliceItem.TYPE_ACTION)) {
-                SliceItem icon = SliceQuery.find(item, SliceItem.TYPE_IMAGE);
+                    && !compareTypes(item, SliceItem.FORMAT_ACTION)) {
+                SliceItem icon = SliceQuery.find(item, SliceItem.FORMAT_IMAGE);
                 if (icon != null) {
                     return icon;
                 }
@@ -78,23 +80,23 @@
     /**
      * @hide
      */
-    public static List<SliceItem> findAll(SliceItem s, int type) {
+    public static List<SliceItem> findAll(SliceItem s, String type) {
         return findAll(s, type, (String[]) null, null);
     }
 
     /**
      * @hide
      */
-    public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) {
+    public static List<SliceItem> findAll(SliceItem s, String type, String hints, String nonHints) {
         return findAll(s, type, new String[]{ hints }, new String[]{ nonHints });
     }
 
     /**
      * @hide
      */
-    public static List<SliceItem> findAll(SliceItem s, int type, String[] hints,
+    public static List<SliceItem> findAll(SliceItem s, String type, String[] hints,
             String[] nonHints) {
-        return stream(s).filter(item -> (type == -1 || item.getType() == type)
+        return stream(s).filter(item -> compareTypes(item, type)
                 && (item.hasHints(hints) && !item.hasAnyHints(nonHints)))
                 .collect(Collectors.toList());
     }
@@ -102,45 +104,45 @@
     /**
      * @hide
      */
-    public static SliceItem find(Slice s, int type, String hints, String nonHints) {
+    public static SliceItem find(Slice s, String type, String hints, String nonHints) {
         return find(s, type, new String[]{ hints }, new String[]{ nonHints });
     }
 
     /**
      * @hide
      */
-    public static SliceItem find(Slice s, int type) {
+    public static SliceItem find(Slice s, String type) {
         return find(s, type, (String[]) null, null);
     }
 
     /**
      * @hide
      */
-    public static SliceItem find(SliceItem s, int type) {
+    public static SliceItem find(SliceItem s, String type) {
         return find(s, type, (String[]) null, null);
     }
 
     /**
      * @hide
      */
-    public static SliceItem find(SliceItem s, int type, String hints, String nonHints) {
+    public static SliceItem find(SliceItem s, String type, String hints, String nonHints) {
         return find(s, type, new String[]{ hints }, new String[]{ nonHints });
     }
 
     /**
      * @hide
      */
-    public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) {
+    public static SliceItem find(Slice s, String type, String[] hints, String[] nonHints) {
         List<String> h = s.getHints();
-        return find(new SliceItem(s, SliceItem.TYPE_SLICE, h.toArray(new String[h.size()])), type,
-                hints, nonHints);
+        return find(new SliceItem(s, SliceItem.FORMAT_SLICE, null, h.toArray(new String[h.size()])),
+                type, hints, nonHints);
     }
 
     /**
      * @hide
      */
-    public static SliceItem find(SliceItem s, int type, String[] hints, String[] nonHints) {
-        return stream(s).filter(item -> (item.getType() == type || type == -1)
+    public static SliceItem find(SliceItem s, String type, String[] hints, String[] nonHints) {
+        return stream(s).filter(item -> compareTypes(item, type)
                 && (item.hasHints(hints) && !item.hasAnyHints(nonHints))).findFirst().orElse(null);
     }
 
@@ -159,8 +161,8 @@
             @Override
             public SliceItem next() {
                 SliceItem item = items.poll();
-                if (item.getType() == SliceItem.TYPE_SLICE
-                        || item.getType() == SliceItem.TYPE_ACTION) {
+                if (compareTypes(item, SliceItem.FORMAT_SLICE)
+                        || compareTypes(item, SliceItem.FORMAT_ACTION)) {
                     items.addAll(item.getSlice().getItems());
                 }
                 return item;
@@ -168,4 +170,19 @@
         };
         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
     }
+
+    /**
+     * @hide
+     */
+    public static boolean compareTypes(SliceItem item, String desiredType) {
+        final int typeLength = desiredType.length();
+        if (typeLength == 3 && desiredType.equals("*/*")) {
+            return true;
+        }
+        if (item.getSubType() == null && desiredType.indexOf('/') < 0) {
+            return item.getFormat().equals(desiredType);
+        }
+        return (item.getFormat() + "/" + item.getSubType())
+                .matches(desiredType.replaceAll("\\*", ".*"));
+    }
 }
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
new file mode 100644
index 0000000..433b67e
--- /dev/null
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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 android.app.slice;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class describing the structure of the data contained within a slice.
+ * <p>
+ * A data version contains a string which describes the type of structure
+ * and a revision which denotes this specific implementation. Revisions are expected
+ * to be backwards compatible and monotonically increasing. Meaning if a
+ * SliceSpec has the same type and an equal or lesser revision,
+ * it is expected to be compatible.
+ * <p>
+ * Apps rendering slices will provide a list of supported versions to the OS which
+ * will also be given to the app. Apps should only return a {@link Slice} with a
+ * {@link SliceSpec} that one of the supported {@link SliceSpec}s provided
+ * {@link #canRender}.
+ *
+ * @see Slice
+ * @see SliceProvider#onBindSlice(Uri)
+ */
+public final class SliceSpec implements Parcelable {
+
+    private final String mType;
+    private final int mRevision;
+
+    public SliceSpec(@NonNull String type, int revision) {
+        mType = type;
+        mRevision = revision;
+    }
+
+    /**
+     * @hide
+     */
+    public SliceSpec(Parcel source) {
+        mType = source.readString();
+        mRevision = source.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mType);
+        dest.writeInt(mRevision);
+    }
+
+    /**
+     * Gets the type of the version.
+     */
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Gets the revision of the version.
+     */
+    public int getRevision() {
+        return mRevision;
+    }
+
+    /**
+     * Indicates that this spec can be used to render the specified spec.
+     * <p>
+     * Rendering support is not bi-directional (e.g. Spec v3 can render
+     * Spec v2, but Spec v2 cannot render Spec v3).
+     *
+     * @param candidate candidate format of data.
+     * @return true if versions are compatible.
+     * @see androidx.app.slice.widget.SliceView
+     */
+    public boolean canRender(@NonNull SliceSpec candidate) {
+        if (!mType.equals(candidate.mType)) return false;
+        return mRevision >= candidate.mRevision;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SliceSpec)) return false;
+        SliceSpec other = (SliceSpec) obj;
+        return mType.equals(other.mType) && mRevision == other.mRevision;
+    }
+
+    public static final Creator<SliceSpec> CREATOR = new Creator<SliceSpec>() {
+        @Override
+        public SliceSpec createFromParcel(Parcel source) {
+            return new SliceSpec(source);
+        }
+
+        @Override
+        public SliceSpec[] newArray(int size) {
+            return new SliceSpec[size];
+        }
+    };
+}
diff --git a/core/java/android/app/slice/widget/ActionRow.java b/core/java/android/app/slice/widget/ActionRow.java
deleted file mode 100644
index c96e6304..0000000
--- a/core/java/android/app/slice/widget/ActionRow.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class ActionRow extends FrameLayout {
-
-    private static final int MAX_ACTIONS = 5;
-    private final int mSize;
-    private final int mIconPadding;
-    private final LinearLayout mActionsGroup;
-    private final boolean mFullActions;
-    private int mColor = Color.BLACK;
-
-    public ActionRow(Context context, boolean fullActions) {
-        super(context);
-        mFullActions = fullActions;
-        mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
-                context.getResources().getDisplayMetrics());
-        mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
-                context.getResources().getDisplayMetrics());
-        mActionsGroup = new LinearLayout(context);
-        mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
-        mActionsGroup.setLayoutParams(
-                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        addView(mActionsGroup);
-    }
-
-    private void setColor(int color) {
-        mColor = color;
-        for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
-            View view = mActionsGroup.getChildAt(i);
-            SliceItem item = (SliceItem) view.getTag();
-            boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
-            if (tint) {
-                ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
-            }
-        }
-    }
-
-    private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
-        ImageView imageView = new ImageView(getContext());
-        imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
-        imageView.setScaleType(ScaleType.FIT_CENTER);
-        imageView.setImageIcon(icon);
-        if (allowTint) {
-            imageView.setImageTintList(ColorStateList.valueOf(mColor));
-        }
-        imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
-                android.R.attr.selectableItemBackground));
-        imageView.setTag(image);
-        addAction(imageView);
-        return imageView;
-    }
-
-    /**
-     * Set the actions and color for this action row.
-     */
-    public void setActions(SliceItem actionRow, SliceItem defColor) {
-        removeAllViews();
-        mActionsGroup.removeAllViews();
-        addView(mActionsGroup);
-
-        SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
-        if (color == null) {
-            color = defColor;
-        }
-        if (color != null) {
-            setColor(color.getColor());
-        }
-        SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
-            if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
-                return;
-            }
-            SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
-            if (image == null) {
-                return;
-            }
-            boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
-            SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
-            if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
-                addAction(image.getIcon(), tint, image).setOnClickListener(
-                        v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
-                createRemoteInputView(mColor, getContext());
-            } else {
-                addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
-                        () -> {
-                            try {
-                                action.getAction().send();
-                            } catch (CanceledException e) {
-                                e.printStackTrace();
-                            }
-                        }));
-            }
-        });
-        setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
-    }
-
-    private void addAction(View child) {
-        mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
-    }
-
-    private void createRemoteInputView(int color, Context context) {
-        View riv = RemoteInputView.inflate(context, this);
-        riv.setVisibility(View.INVISIBLE);
-        addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        riv.setBackgroundColor(color);
-    }
-
-    private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
-            RemoteInput input) {
-        if (input == null) {
-            return false;
-        }
-
-        ViewParent p = view.getParent().getParent();
-        RemoteInputView riv = null;
-        while (p != null) {
-            if (p instanceof View) {
-                View pv = (View) p;
-                riv = findRemoteInputView(pv);
-                if (riv != null) {
-                    break;
-                }
-            }
-            p = p.getParent();
-        }
-        if (riv == null) {
-            return false;
-        }
-
-        int width = view.getWidth();
-        if (view instanceof TextView) {
-            // Center the reveal on the text which might be off-center from the TextView
-            TextView tv = (TextView) view;
-            if (tv.getLayout() != null) {
-                int innerWidth = (int) tv.getLayout().getLineWidth(0);
-                innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
-                width = Math.min(width, innerWidth);
-            }
-        }
-        int cx = view.getLeft() + width / 2;
-        int cy = view.getTop() + view.getHeight() / 2;
-        int w = riv.getWidth();
-        int h = riv.getHeight();
-        int r = Math.max(
-                Math.max(cx + cy, cx + (h - cy)),
-                Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
-        riv.setRevealParameters(cx, cy, r);
-        riv.setPendingIntent(pendingIntent);
-        riv.setRemoteInput(new RemoteInput[] {
-                input
-        }, input);
-        riv.focusAnimated();
-        return true;
-    }
-
-    private RemoteInputView findRemoteInputView(View v) {
-        if (v == null) {
-            return null;
-        }
-        return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
-    }
-}
diff --git a/core/java/android/app/slice/widget/GridView.java b/core/java/android/app/slice/widget/GridView.java
deleted file mode 100644
index 793abc0..0000000
--- a/core/java/android/app/slice/widget/GridView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class GridView extends LinearLayout implements SliceListView {
-
-    private static final String TAG = "GridView";
-
-    private static final int MAX_IMAGES = 3;
-    private static final int MAX_ALL = 5;
-    private boolean mIsAllImages;
-
-    public GridView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mIsAllImages) {
-            int width = MeasureSpec.getSize(widthMeasureSpec);
-            int height = width / getChildCount();
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
-                    height);
-            getLayoutParams().height = height;
-            for (int i = 0; i < getChildCount(); i++) {
-                getChildAt(i).getLayoutParams().height = height;
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        mIsAllImages = true;
-        removeAllViews();
-        int total = 1;
-        if (slice.getType() == SliceItem.TYPE_SLICE) {
-            List<SliceItem> items = slice.getSlice().getItems();
-            total = items.size();
-            for (int i = 0; i < total; i++) {
-                SliceItem item = items.get(i);
-                if (isFull()) {
-                    continue;
-                }
-                if (!addItem(item)) {
-                    mIsAllImages = false;
-                }
-            }
-        } else {
-            if (!isFull()) {
-                if (!addItem(slice)) {
-                    mIsAllImages = false;
-                }
-            }
-        }
-        if (total > getChildCount() && mIsAllImages) {
-            addExtraCount(total - getChildCount());
-        }
-    }
-
-    private void addExtraCount(int numExtra) {
-        View last = getChildAt(getChildCount() - 1);
-        FrameLayout frame = new FrameLayout(getContext());
-        frame.setLayoutParams(last.getLayoutParams());
-
-        removeView(last);
-        frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-        TextView v = new TextView(getContext());
-        v.setTextColor(Color.WHITE);
-        v.setBackgroundColor(0x4d000000);
-        v.setText(getResources().getString(R.string.slice_more_content, numExtra));
-        v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
-        v.setGravity(Gravity.CENTER);
-        frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-        addView(frame);
-    }
-
-    private boolean isFull() {
-        return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
-    }
-
-    /**
-     * Returns true if this item is just an image.
-     */
-    private boolean addItem(SliceItem item) {
-        if (item.hasHint(Slice.HINT_HIDDEN)) {
-            return false;
-        }
-        if (item.getType() == SliceItem.TYPE_IMAGE) {
-            ImageView v = new ImageView(getContext());
-            v.setImageIcon(item.getIcon());
-            v.setScaleType(ScaleType.CENTER_CROP);
-            addView(v, new LayoutParams(0, MATCH_PARENT, 1));
-            return true;
-        } else {
-            LinearLayout v = new LinearLayout(getContext());
-            int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    12, getContext().getResources().getDisplayMetrics());
-            v.setPadding(0, s, 0, 0);
-            v.setOrientation(LinearLayout.VERTICAL);
-            v.setGravity(Gravity.CENTER_HORIZONTAL);
-            // TODO: Unify sporadic inflates that happen throughout the code.
-            ArrayList<SliceItem> items = new ArrayList<>();
-            if (item.getType() == SliceItem.TYPE_SLICE) {
-                items.addAll(item.getSlice().getItems());
-            }
-            items.forEach(i -> {
-                if (i.hasHint(Slice.HINT_HIDDEN)) {
-                    return;
-                }
-                Context context = getContext();
-                switch (i.getType()) {
-                    case SliceItem.TYPE_TEXT:
-                        boolean title = false;
-                        if ((item.hasAnyHints(new String[] {
-                                Slice.HINT_LARGE, Slice.HINT_TITLE
-                        }))) {
-                            title = true;
-                        }
-                        TextView tv = (TextView) LayoutInflater.from(context).inflate(
-                                title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
-                        tv.setText(i.getText());
-                        v.addView(tv);
-                        break;
-                    case SliceItem.TYPE_IMAGE:
-                        ImageView iv = new ImageView(context);
-                        iv.setImageIcon(i.getIcon());
-                        if (item.hasHint(Slice.HINT_LARGE)) {
-                            iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-                        } else {
-                            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                                    48, context.getResources().getDisplayMetrics());
-                            iv.setLayoutParams(new LayoutParams(size, size));
-                        }
-                        v.addView(iv);
-                        break;
-                    case SliceItem.TYPE_REMOTE_VIEW:
-                        v.addView(i.getRemoteView().apply(context, v));
-                        break;
-                    case SliceItem.TYPE_COLOR:
-                        // TODO: Support color to tint stuff here.
-                        break;
-                }
-            });
-            addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
-            return false;
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/LargeSliceAdapter.java b/core/java/android/app/slice/widget/LargeSliceAdapter.java
deleted file mode 100644
index 267fff6..0000000
--- a/core/java/android/app/slice/widget/LargeSliceAdapter.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceViewHolder;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.RecyclerView;
-import com.android.internal.widget.RecyclerView.ViewHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @hide
- */
-public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
-
-    public static final int TYPE_DEFAULT       = 1;
-    public static final int TYPE_HEADER        = 2;
-    public static final int TYPE_GRID          = 3;
-    public static final int TYPE_MESSAGE       = 4;
-    public static final int TYPE_MESSAGE_LOCAL = 5;
-    public static final int TYPE_REMOTE_VIEWS  = 6;
-
-    private final IdGenerator mIdGen = new IdGenerator();
-    private final Context mContext;
-    private List<SliceWrapper> mSlices = new ArrayList<>();
-    private SliceItem mColor;
-
-    public LargeSliceAdapter(Context context) {
-        mContext = context;
-        setHasStableIds(true);
-    }
-
-    /**
-     * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
-     */
-    public void setSliceItems(List<SliceItem> slices, SliceItem color) {
-        mColor = color;
-        mIdGen.resetUsage();
-        mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
-                .collect(Collectors.toList());
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View v = inflateForType(viewType);
-        v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        return new SliceViewHolder(v);
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return mSlices.get(position).mType;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return mSlices.get(position).mId;
-    }
-
-    @Override
-    public int getItemCount() {
-        return mSlices.size();
-    }
-
-    @Override
-    public void onBindViewHolder(SliceViewHolder holder, int position) {
-        SliceWrapper slice = mSlices.get(position);
-        if (holder.mSliceView != null) {
-            holder.mSliceView.setColor(mColor);
-            holder.mSliceView.setSliceItem(slice.mItem);
-        } else if (slice.mType == TYPE_REMOTE_VIEWS) {
-            FrameLayout frame = (FrameLayout) holder.itemView;
-            frame.removeAllViews();
-            frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
-        }
-    }
-
-    private View inflateForType(int viewType) {
-        switch (viewType) {
-            case TYPE_REMOTE_VIEWS:
-                return new FrameLayout(mContext);
-            case TYPE_GRID:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
-            case TYPE_MESSAGE:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
-            case TYPE_MESSAGE_LOCAL:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
-        }
-        return new SmallTemplateView(mContext);
-    }
-
-    protected static class SliceWrapper {
-        private final SliceItem mItem;
-        private final int mType;
-        private final long mId;
-
-        public SliceWrapper(SliceItem item, IdGenerator idGen) {
-            mItem = item;
-            mType = getType(item);
-            mId = idGen.getId(item);
-        }
-
-        public static int getType(SliceItem item) {
-            if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
-                return TYPE_REMOTE_VIEWS;
-            }
-            if (item.hasHint(Slice.HINT_MESSAGE)) {
-                // TODO: Better way to determine me or not? Something more like Messaging style.
-                if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
-                    return TYPE_MESSAGE;
-                } else {
-                    return TYPE_MESSAGE_LOCAL;
-                }
-            }
-            if (item.hasHint(Slice.HINT_HORIZONTAL)) {
-                return TYPE_GRID;
-            }
-            return TYPE_DEFAULT;
-        }
-    }
-
-    /**
-     * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
-     */
-    public static class SliceViewHolder extends ViewHolder {
-        public final SliceListView mSliceView;
-
-        public SliceViewHolder(View itemView) {
-            super(itemView);
-            mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
-        }
-    }
-
-    /**
-     * View slices being displayed in {@link LargeSliceAdapter}.
-     */
-    public interface SliceListView {
-        /**
-         * Set the slice item for this view.
-         */
-        void setSliceItem(SliceItem slice);
-
-        /**
-         * Set the color for the items in this view.
-         */
-        default void setColor(SliceItem color) {
-
-        }
-    }
-
-    private static class IdGenerator {
-        private long mNextLong = 0;
-        private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
-        private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
-
-        public long getId(SliceItem item) {
-            String str = genString(item);
-            if (!mCurrentIds.containsKey(str)) {
-                mCurrentIds.put(str, mNextLong++);
-            }
-            long id = mCurrentIds.get(str);
-            int index = mUsedIds.getOrDefault(str, 0);
-            mUsedIds.put(str, index + 1);
-            return id + index * 10000;
-        }
-
-        private String genString(SliceItem item) {
-            StringBuilder builder = new StringBuilder();
-            SliceQuery.stream(item).forEach(i -> {
-                builder.append(i.getType());
-                i.removeHint(Slice.HINT_SELECTED);
-                builder.append(i.getHints());
-                switch (i.getType()) {
-                    case SliceItem.TYPE_REMOTE_VIEW:
-                        builder.append(i.getRemoteView());
-                        break;
-                    case SliceItem.TYPE_IMAGE:
-                        builder.append(i.getIcon());
-                        break;
-                    case SliceItem.TYPE_TEXT:
-                        builder.append(i.getText());
-                        break;
-                    case SliceItem.TYPE_COLOR:
-                        builder.append(i.getColor());
-                        break;
-                }
-            });
-            return builder.toString();
-        }
-
-        public void resetUsage() {
-            mUsedIds.clear();
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/LargeTemplateView.java b/core/java/android/app/slice/widget/LargeTemplateView.java
deleted file mode 100644
index 788f6fb..0000000
--- a/core/java/android/app/slice/widget/LargeTemplateView.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.util.TypedValue;
-
-import com.android.internal.widget.LinearLayoutManager;
-import com.android.internal.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class LargeTemplateView extends SliceModeView {
-
-    private final LargeSliceAdapter mAdapter;
-    private final RecyclerView mRecyclerView;
-    private final int mDefaultHeight;
-    private final int mMaxHeight;
-    private Slice mSlice;
-    private boolean mIsScrollable;
-
-    public LargeTemplateView(Context context) {
-        super(context);
-
-        mRecyclerView = new RecyclerView(getContext());
-        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
-        mAdapter = new LargeSliceAdapter(context);
-        mRecyclerView.setAdapter(mAdapter);
-        addView(mRecyclerView);
-        mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
-                getResources().getDisplayMetrics());
-        mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
-                getResources().getDisplayMetrics());
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_LARGE;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mRecyclerView.getMeasuredHeight() > mMaxHeight
-                || (mSlice != null && mSlice.hasHint(Slice.HINT_PARTIAL))) {
-            mRecyclerView.getLayoutParams().height = mDefaultHeight;
-        } else {
-            mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        mSlice = slice;
-        List<SliceItem> items = new ArrayList<>();
-        boolean[] hasHeader = new boolean[1];
-        if (slice.hasHint(Slice.HINT_LIST)) {
-            addList(slice, items);
-        } else {
-            slice.getItems().forEach(item -> {
-                if (item.hasHint(Slice.HINT_HIDDEN)) {
-                    // If it's hidden we don't show it
-                    return;
-                } else if (item.hasHint(Slice.HINT_ACTIONS)) {
-                    // Action groups don't show in lists
-                    return;
-                } else if (item.getType() == SliceItem.TYPE_COLOR) {
-                    // A color is not a list item
-                    return;
-                } else if (item.getType() == SliceItem.TYPE_SLICE
-                        && item.hasHint(Slice.HINT_LIST)) {
-                    addList(item.getSlice(), items);
-                } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
-                    items.add(item);
-                } else if (!hasHeader[0]) {
-                    hasHeader[0] = true;
-                    items.add(0, item);
-                } else {
-                    item.addHint(Slice.HINT_LIST_ITEM);
-                    items.add(item);
-                }
-            });
-        }
-        mAdapter.setSliceItems(items, color);
-    }
-
-    private void addList(Slice slice, List<SliceItem> items) {
-        List<SliceItem> sliceItems = slice.getItems();
-        sliceItems.forEach(i -> {
-            if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
-                i.addHint(Slice.HINT_LIST_ITEM);
-                items.add(i);
-            }
-        });
-    }
-
-    /**
-     * Whether or not the content in this template should be scrollable.
-     */
-    public void setScrollable(boolean isScrollable) {
-        // TODO -- restrict / enable how much this view can show
-        mIsScrollable = isScrollable;
-    }
-}
diff --git a/core/java/android/app/slice/widget/MessageView.java b/core/java/android/app/slice/widget/MessageView.java
deleted file mode 100644
index 3124398..0000000
--- a/core/java/android/app/slice/widget/MessageView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.text.SpannableStringBuilder;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class MessageView extends LinearLayout implements SliceListView {
-
-    private TextView mDetails;
-    private ImageView mIcon;
-
-    public MessageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDetails = findViewById(android.R.id.summary);
-        mIcon = findViewById(android.R.id.icon);
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
-        if (source != null) {
-            final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    24, getContext().getResources().getDisplayMetrics());
-            // TODO try and turn this into a drawable
-            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
-            Canvas iconCanvas = new Canvas(iconBm);
-            Drawable d = source.getIcon().loadDrawable(getContext());
-            d.setBounds(0, 0, iconSize, iconSize);
-            d.draw(iconCanvas);
-            mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
-        }
-        SpannableStringBuilder builder = new SpannableStringBuilder();
-        SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
-            if (builder.length() != 0) {
-                builder.append('\n');
-            }
-            builder.append(text.getText());
-        });
-        mDetails.setText(builder.toString());
-    }
-
-}
diff --git a/core/java/android/app/slice/widget/RemoteInputView.java b/core/java/android/app/slice/widget/RemoteInputView.java
deleted file mode 100644
index 6eff5af..0000000
--- a/core/java/android/app/slice/widget/RemoteInputView.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.animation.Animator;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutManager;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.R;
-
-/**
- * Host for the remote input.
- *
- * @hide
- */
-// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
-public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
-
-    private static final String TAG = "RemoteInput";
-
-    /**
-     * A marker object that let's us easily find views of this class.
-     */
-    public static final Object VIEW_TAG = new Object();
-
-    private RemoteEditText mEditText;
-    private ImageButton mSendButton;
-    private ProgressBar mProgressBar;
-    private PendingIntent mPendingIntent;
-    private RemoteInput[] mRemoteInputs;
-    private RemoteInput mRemoteInput;
-
-    private int mRevealCx;
-    private int mRevealCy;
-    private int mRevealR;
-    private boolean mResetting;
-
-    public RemoteInputView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mProgressBar = findViewById(R.id.remote_input_progress);
-        mSendButton = findViewById(R.id.remote_input_send);
-        mSendButton.setOnClickListener(this);
-
-        mEditText = (RemoteEditText) getChildAt(0);
-        mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-                final boolean isSoftImeEvent = event == null
-                        && (actionId == EditorInfo.IME_ACTION_DONE
-                                || actionId == EditorInfo.IME_ACTION_NEXT
-                                || actionId == EditorInfo.IME_ACTION_SEND);
-                final boolean isKeyboardEnterKey = event != null
-                        && KeyEvent.isConfirmKey(event.getKeyCode())
-                        && event.getAction() == KeyEvent.ACTION_DOWN;
-
-                if (isSoftImeEvent || isKeyboardEnterKey) {
-                    if (mEditText.length() > 0) {
-                        sendRemoteInput();
-                    }
-                    // Consume action to prevent IME from closing.
-                    return true;
-                }
-                return false;
-            }
-        });
-        mEditText.addTextChangedListener(this);
-        mEditText.setInnerFocusable(false);
-        mEditText.mRemoteInputView = this;
-    }
-
-    private void sendRemoteInput() {
-        Bundle results = new Bundle();
-        results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
-        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
-                results);
-
-        mEditText.setEnabled(false);
-        mSendButton.setVisibility(INVISIBLE);
-        mProgressBar.setVisibility(VISIBLE);
-        mEditText.mShowImeOnInputConnection = false;
-
-        // Tell ShortcutManager that this package has been "activated".  ShortcutManager
-        // will reset the throttling for this package.
-        // Strictly speaking, the intent receiver may be different from the intent creator,
-        // but that's an edge case, and also because we can't always know which package will receive
-        // an intent, so we just reset for the creator.
-        getContext().getSystemService(ShortcutManager.class).onApplicationActive(
-                mPendingIntent.getCreatorPackage(),
-                getContext().getUserId());
-
-        try {
-            mPendingIntent.send(mContext, 0, fillInIntent);
-            reset();
-        } catch (PendingIntent.CanceledException e) {
-            Log.i(TAG, "Unable to send remote input result", e);
-            Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
-                    Toast.LENGTH_SHORT).show();
-            reset();
-        }
-    }
-
-    /**
-     * Creates a remote input view.
-     */
-    public static RemoteInputView inflate(Context context, ViewGroup root) {
-        RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
-                R.layout.slice_remote_input, root, false);
-        v.setTag(VIEW_TAG);
-        return v;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mSendButton) {
-            sendRemoteInput();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        super.onTouchEvent(event);
-
-        // We never want for a touch to escape to an outer view or one we covered.
-        return true;
-    }
-
-    private void onDefocus() {
-        setVisibility(INVISIBLE);
-    }
-
-    /**
-     * Set the pending intent for remote input.
-     */
-    public void setPendingIntent(PendingIntent pendingIntent) {
-        mPendingIntent = pendingIntent;
-    }
-
-    /**
-     * Set the remote inputs for this view.
-     */
-    public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
-        mRemoteInputs = remoteInputs;
-        mRemoteInput = remoteInput;
-        mEditText.setHint(mRemoteInput.getLabel());
-    }
-
-    /**
-     * Focuses the remote input view.
-     */
-    public void focusAnimated() {
-        if (getVisibility() != VISIBLE) {
-            Animator animator = ViewAnimationUtils.createCircularReveal(
-                    this, mRevealCx, mRevealCy, 0, mRevealR);
-            animator.setDuration(200);
-            animator.start();
-        }
-        focus();
-    }
-
-    private void focus() {
-        setVisibility(VISIBLE);
-        mEditText.setInnerFocusable(true);
-        mEditText.mShowImeOnInputConnection = true;
-        mEditText.setSelection(mEditText.getText().length());
-        mEditText.requestFocus();
-        updateSendButton();
-    }
-
-    private void reset() {
-        mResetting = true;
-
-        mEditText.getText().clear();
-        mEditText.setEnabled(true);
-        mSendButton.setVisibility(VISIBLE);
-        mProgressBar.setVisibility(INVISIBLE);
-        updateSendButton();
-        onDefocus();
-
-        mResetting = false;
-    }
-
-    @Override
-    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
-        if (mResetting && child == mEditText) {
-            // Suppress text events if it happens during resetting. Ideally this would be
-            // suppressed by the text view not being shown, but that doesn't work here because it
-            // needs to stay visible for the animation.
-            return false;
-        }
-        return super.onRequestSendAccessibilityEvent(child, event);
-    }
-
-    private void updateSendButton() {
-        mSendButton.setEnabled(mEditText.getText().length() != 0);
-    }
-
-    @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-    }
-
-    @Override
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-    }
-
-    @Override
-    public void afterTextChanged(Editable s) {
-        updateSendButton();
-    }
-
-    /**
-     * Tries to find an action that matches the current pending intent of this view and updates its
-     * state to that of the found action
-     *
-     * @return true if a matching action was found, false otherwise
-     */
-    public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
-        if (mPendingIntent == null || actions == null) {
-            return false;
-        }
-        Intent current = mPendingIntent.getIntent();
-        if (current == null) {
-            return false;
-        }
-
-        for (Notification.Action a : actions) {
-            RemoteInput[] inputs = a.getRemoteInputs();
-            if (a.actionIntent == null || inputs == null) {
-                continue;
-            }
-            Intent candidate = a.actionIntent.getIntent();
-            if (!current.filterEquals(candidate)) {
-                continue;
-            }
-
-            RemoteInput input = null;
-            for (RemoteInput i : inputs) {
-                if (i.getAllowFreeFormInput()) {
-                    input = i;
-                }
-            }
-            if (input == null) {
-                continue;
-            }
-            setPendingIntent(a.actionIntent);
-            setRemoteInput(inputs, input);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    public void setRevealParameters(int cx, int cy, int r) {
-        mRevealCx = cx;
-        mRevealCy = cy;
-        mRevealR = r;
-    }
-
-    @Override
-    public void dispatchStartTemporaryDetach() {
-        super.dispatchStartTemporaryDetach();
-        // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
-        // won't lose IME focus.
-        detachViewFromParent(mEditText);
-    }
-
-    @Override
-    public void dispatchFinishTemporaryDetach() {
-        if (isAttachedToWindow()) {
-            attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
-        } else {
-            removeDetachedView(mEditText, false /* animate */);
-        }
-        super.dispatchFinishTemporaryDetach();
-    }
-
-    /**
-     * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
-     * whenever the user navigates away from it or it becomes invisible.
-     */
-    public static class RemoteEditText extends EditText {
-
-        private final Drawable mBackground;
-        private RemoteInputView mRemoteInputView;
-        boolean mShowImeOnInputConnection;
-
-        public RemoteEditText(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            mBackground = getBackground();
-        }
-
-        private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null || isTemporarilyDetached()) {
-                if (isTemporarilyDetached()) {
-                    // We might get reattached but then the other one of HUN / expanded might steal
-                    // our focus, so we'll need to save our text here.
-                }
-                return;
-            }
-            if (isFocusable() && isEnabled()) {
-                setInnerFocusable(false);
-                if (mRemoteInputView != null) {
-                    mRemoteInputView.onDefocus();
-                }
-                mShowImeOnInputConnection = false;
-            }
-        }
-
-        @Override
-        protected void onVisibilityChanged(View changedView, int visibility) {
-            super.onVisibilityChanged(changedView, visibility);
-
-            if (!isShown()) {
-                defocusIfNeeded(false /* animate */);
-            }
-        }
-
-        @Override
-        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-            super.onFocusChanged(focused, direction, previouslyFocusedRect);
-            if (!focused) {
-                defocusIfNeeded(true /* animate */);
-            }
-        }
-
-        @Override
-        public void getFocusedRect(Rect r) {
-            super.getFocusedRect(r);
-            r.top = mScrollY;
-            r.bottom = mScrollY + (mBottom - mTop);
-        }
-
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                // Eat the DOWN event here to prevent any default behavior.
-                return true;
-            }
-            return super.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public boolean onKeyUp(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                defocusIfNeeded(true /* animate */);
-                return true;
-            }
-            return super.onKeyUp(keyCode, event);
-        }
-
-        @Override
-        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-
-            if (mShowImeOnInputConnection && inputConnection != null) {
-                final InputMethodManager imm = InputMethodManager.getInstance();
-                if (imm != null) {
-                    // onCreateInputConnection is called by InputMethodManager in the middle of
-                    // setting up the connection to the IME; wait with requesting the IME until that
-                    // work has completed.
-                    post(new Runnable() {
-                        @Override
-                        public void run() {
-                            imm.viewClicked(RemoteEditText.this);
-                            imm.showSoftInput(RemoteEditText.this, 0);
-                        }
-                    });
-                }
-            }
-
-            return inputConnection;
-        }
-
-        @Override
-        public void onCommitCompletion(CompletionInfo text) {
-            clearComposingText();
-            setText(text.getText());
-            setSelection(getText().length());
-        }
-
-        void setInnerFocusable(boolean focusable) {
-            setFocusableInTouchMode(focusable);
-            setFocusable(focusable);
-            setCursorVisible(focusable);
-
-            if (focusable) {
-                requestFocus();
-                setBackground(mBackground);
-            } else {
-                setBackground(null);
-            }
-
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/ShortcutView.java b/core/java/android/app/slice/widget/ShortcutView.java
deleted file mode 100644
index 0b7ad0d..0000000
--- a/core/java/android/app/slice/widget/ShortcutView.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
-import android.net.Uri;
-
-import com.android.internal.R;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public class ShortcutView extends SliceModeView {
-
-    private static final String TAG = "ShortcutView";
-
-    private Uri mUri;
-    private PendingIntent mAction;
-    private SliceItem mLabel;
-    private SliceItem mIcon;
-
-    private int mLargeIconSize;
-    private int mSmallIconSize;
-
-    public ShortcutView(Context context) {
-        super(context);
-        final Resources res = getResources();
-        mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
-        mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        removeAllViews();
-        determineShortcutItems(getContext(), slice);
-        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        if (colorItem == null) {
-            colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        }
-        // TODO: pick better default colour
-        final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
-        ShapeDrawable circle = new ShapeDrawable(new OvalShape());
-        circle.setTint(color);
-        setBackground(circle);
-        if (mIcon != null) {
-            final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
-            final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
-            SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
-                    isLarge, this /* parent */);
-            mUri = slice.getUri();
-            setClickable(true);
-        } else {
-            setClickable(false);
-        }
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_SHORTCUT;
-    }
-
-    @Override
-    public boolean performClick() {
-        if (!callOnClick()) {
-            try {
-                if (mAction != null) {
-                    mAction.send();
-                } else {
-                    Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    getContext().startActivity(intent);
-                }
-            } catch (CanceledException e) {
-                e.printStackTrace();
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Looks at the slice and determines which items are best to use to compose the shortcut.
-     */
-    private void determineShortcutItems(Context context, Slice slice) {
-        List<String> h = slice.getHints();
-        SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
-                h.toArray(new String[h.size()]));
-        SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
-                Slice.HINT_TITLE, null);
-
-        if (titleItem != null) {
-            // Preferred case: hinted action containing hinted image and text
-            mAction = titleItem.getAction();
-            mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
-                    null);
-            mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
-                    null);
-        } else {
-            // No hinted action; just use the first one
-            SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
-                    null);
-            mAction = (actionItem != null) ? actionItem.getAction() : null;
-        }
-        // First fallback: any hinted image and text
-        if (mIcon == null) {
-            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
-                    null);
-        }
-        if (mLabel == null) {
-            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
-                    null);
-        }
-        // Second fallback: first image and text
-        if (mIcon == null) {
-            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
-                    null);
-        }
-        if (mLabel == null) {
-            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
-                    null);
-        }
-        // Final fallback: use app info
-        if (mIcon == null || mLabel == null || mAction == null) {
-            PackageManager pm = context.getPackageManager();
-            ProviderInfo providerInfo = pm.resolveContentProvider(
-                    slice.getUri().getAuthority(), 0);
-            ApplicationInfo appInfo = providerInfo.applicationInfo;
-            if (appInfo != null) {
-                if (mIcon == null) {
-                    Drawable icon = appInfo.loadDefaultIcon(pm);
-                    mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
-                            SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
-                }
-                if (mLabel == null) {
-                    mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
-                            SliceItem.TYPE_TEXT, null);
-                }
-                if (mAction == null) {
-                    mAction = PendingIntent.getActivity(context, 0,
-                            pm.getLaunchIntentForPackage(appInfo.packageName), 0);
-                }
-            }
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java
deleted file mode 100644
index fa1b64c..0000000
--- a/core/java/android/app/slice/widget/SliceView.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * A view for displaying a {@link Slice} which is a piece of app content and actions. SliceView is
- * able to present slice content in a templated format outside of the associated app. The way this
- * content is displayed depends on the structure of the slice, the hints associated with the
- * content, and the mode that SliceView is configured for. The modes that SliceView supports are:
- * <ul>
- * <li><b>Shortcut</b>: A shortcut is presented as an icon and a text label representing the main
- * content or action associated with the slice.</li>
- * <li><b>Small</b>: The small format has a restricted height and can present a single
- * {@link SliceItem} or a limited collection of items.</li>
- * <li><b>Large</b>: The large format displays multiple small templates in a list, if scrolling is
- * not enabled (see {@link #setScrollable(boolean)}) the view will show as many items as it can
- * comfortably fit.</li>
- * </ul>
- * <p>
- * When constructing a slice, the contents of it can be annotated with hints, these provide the OS
- * with some information on how the content should be displayed. For example, text annotated with
- * {@link Slice#HINT_TITLE} would be placed in the title position of a template. A slice annotated
- * with {@link Slice#HINT_LIST} would present the child items of that slice in a list.
- * <p>
- * SliceView can be provided a slice via a uri {@link #setSlice(Uri)} in which case a content
- * observer will be set for that uri and the view will update if there are any changes to the slice.
- * To use this the app must have a special permission to bind to the slice (see
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Example usage:
- *
- * <pre class="prettyprint">
- * SliceView v = new SliceView(getContext());
- * v.setMode(desiredMode);
- * v.setSlice(sliceUri);
- * </pre>
- */
-public class SliceView extends ViewGroup {
-
-    private static final String TAG = "SliceView";
-
-    /**
-     * @hide
-     */
-    public abstract static class SliceModeView extends FrameLayout {
-
-        public SliceModeView(Context context) {
-            super(context);
-        }
-
-        /**
-         * @return the mode of the slice being presented.
-         */
-        public abstract String getMode();
-
-        /**
-         * @param slice the slice to show in this view.
-         */
-        public abstract void setSlice(Slice slice);
-    }
-
-    /**
-     * @hide
-     */
-    @StringDef({
-            MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
-    })
-    public @interface SliceMode {}
-
-    /**
-     * Mode indicating this slice should be presented in small template format.
-     */
-    public static final String MODE_SMALL       = "SLICE_SMALL";
-    /**
-     * Mode indicating this slice should be presented in large template format.
-     */
-    public static final String MODE_LARGE       = "SLICE_LARGE";
-    /**
-     * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
-     * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
-     * slice.
-     */
-    public static final String MODE_SHORTCUT    = "SLICE_ICON";
-
-    /**
-     * Will select the type of slice binding based on size of the View. TODO: Put in some info about
-     * that selection.
-     */
-    private static final String MODE_AUTO = "auto";
-
-    private String mMode = MODE_AUTO;
-    private SliceModeView mCurrentView;
-    private final ActionRow mActions;
-    private Slice mCurrentSlice;
-    private boolean mShowActions = true;
-    private boolean mIsScrollable;
-    private SliceObserver mObserver;
-    private final int mShortcutSize;
-
-    public SliceView(Context context) {
-        this(context, null);
-    }
-
-    public SliceView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
-        mActions = new ActionRow(mContext, true);
-        mActions.setBackground(new ColorDrawable(0xffeeeeee));
-        mCurrentView = new LargeTemplateView(mContext);
-        addView(mCurrentView, getChildLp(mCurrentView));
-        addView(mActions, getChildLp(mActions));
-        mShortcutSize = getContext().getResources()
-                .getDimensionPixelSize(R.dimen.slice_shortcut_size);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-        int actionHeight = mActions.getVisibility() != View.GONE
-                ? mActions.getMeasuredHeight()
-                : 0;
-        int newHeightSpec = MeasureSpec.makeMeasureSpec(
-                mCurrentView.getMeasuredHeight() + actionHeight, MeasureSpec.EXACTLY);
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        setMeasuredDimension(width, newHeightSpec);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        mCurrentView.layout(l, t, l + mCurrentView.getMeasuredWidth(),
-                t + mCurrentView.getMeasuredHeight());
-        if (mActions.getVisibility() != View.GONE) {
-            mActions.layout(l, mCurrentView.getMeasuredHeight(), l + mActions.getMeasuredWidth(),
-                    mCurrentView.getMeasuredHeight() + mActions.getMeasuredHeight());
-        }
-    }
-
-    /**
-     * Populates this view with the {@link Slice} associated with the provided {@link Intent}. To
-     * use this method your app must have the permission
-     * {@link android.Manifest.permission#BIND_SLICE}).
-     * <p>
-     * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
-     * updated with the slice identified by the provided intent changes. The lifecycle of this
-     * observer is handled by SliceView in {@link #onAttachedToWindow()} and
-     * {@link #onDetachedFromWindow()}. To unregister this observer outside of that you can call
-     * {@link #clearSlice}.
-     *
-     * @return true if a slice was found for the provided intent.
-     * @hide
-     */
-    public boolean setSlice(@Nullable Intent intent) {
-        Slice s = Slice.bindSlice(mContext, intent);
-        if (s != null) {
-            return setSlice(s.getUri());
-        }
-        return s != null;
-    }
-
-    /**
-     * Populates this view with the {@link Slice} associated with the provided {@link Uri}. To use
-     * this method your app must have the permission
-     * {@link android.Manifest.permission#BIND_SLICE}).
-     * <p>
-     * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
-     * updated when the slice identified by the provided URI changes. The lifecycle of this observer
-     * is handled by SliceView in {@link #onAttachedToWindow()} and {@link #onDetachedFromWindow()}.
-     * To unregister this observer outside of that you can call {@link #clearSlice}.
-     *
-     * @return true if a slice was found for the provided uri.
-     */
-    public boolean setSlice(@NonNull Uri sliceUri) {
-        Preconditions.checkNotNull(sliceUri,
-                "Uri cannot be null, to remove the slice use clearSlice()");
-        if (sliceUri == null) {
-            clearSlice();
-            return false;
-        }
-        validate(sliceUri);
-        Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri);
-        if (s != null) {
-            if (mObserver != null) {
-                getContext().getContentResolver().unregisterContentObserver(mObserver);
-            }
-            mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
-            if (isAttachedToWindow()) {
-                registerSlice(sliceUri);
-            }
-            mCurrentSlice = s;
-            reinflate();
-        }
-        return s != null;
-    }
-
-    /**
-     * Populates this view to the provided {@link Slice}.
-     * <p>
-     * This does not register a content observer on the URI that the slice is backed by so it will
-     * not update if the content changes. To have the view update when the content changes use
-     * {@link #setSlice(Uri)} instead. Unlike {@link #setSlice(Uri)}, this method does not require
-     * any special permissions.
-     */
-    public void showSlice(@NonNull Slice slice) {
-        Preconditions.checkNotNull(slice,
-                "Slice cannot be null, to remove the slice use clearSlice()");
-        clearSlice();
-        mCurrentSlice = slice;
-        reinflate();
-    }
-
-    /**
-     * Unregisters the change observer that is set when using {@link #setSlice}. Normally this is
-     * done automatically during {@link #onDetachedFromWindow()}.
-     * <p>
-     * It is safe to call this method multiple times.
-     */
-    public void clearSlice() {
-        mCurrentSlice = null;
-        if (mObserver != null) {
-            getContext().getContentResolver().unregisterContentObserver(mObserver);
-            mObserver = null;
-        }
-    }
-
-    /**
-     * Set the mode this view should present in.
-     */
-    public void setMode(@SliceMode String mode) {
-        setMode(mode, false /* animate */);
-    }
-
-    /**
-     * Set whether this view should allow scrollable content when presenting in {@link #MODE_LARGE}.
-     */
-    public void setScrollable(boolean isScrollable) {
-        mIsScrollable = isScrollable;
-        reinflate();
-    }
-
-    /**
-     * @hide
-     */
-    public void setMode(@SliceMode String mode, boolean animate) {
-        if (animate) {
-            Log.e(TAG, "Animation not supported yet");
-        }
-        mMode = mode;
-        reinflate();
-    }
-
-    /**
-     * @return the mode this view is presenting in.
-     */
-    public @SliceMode String getMode() {
-        if (mMode.equals(MODE_AUTO)) {
-            return MODE_LARGE;
-        }
-        return mMode;
-    }
-
-    /**
-     * @hide
-     *
-     * Whether this view should show a row of actions with it.
-     */
-    public void setShowActionRow(boolean show) {
-        mShowActions = show;
-        reinflate();
-    }
-
-    private SliceModeView createView(String mode) {
-        switch (mode) {
-            case MODE_SHORTCUT:
-                return new ShortcutView(getContext());
-            case MODE_SMALL:
-                return new SmallTemplateView(getContext());
-        }
-        return new LargeTemplateView(getContext());
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        registerSlice(mCurrentSlice != null ? mCurrentSlice.getUri() : null);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mObserver != null) {
-            getContext().getContentResolver().unregisterContentObserver(mObserver);
-            mObserver = null;
-        }
-    }
-
-    private void registerSlice(Uri sliceUri) {
-        if (sliceUri == null || mObserver == null) {
-            return;
-        }
-        mContext.getContentResolver().registerContentObserver(sliceUri,
-                false /* notifyForDescendants */, mObserver);
-    }
-
-    private void reinflate() {
-        if (mCurrentSlice == null) {
-            return;
-        }
-        // TODO: Smarter mapping here from one state to the next.
-        SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
-        List<SliceItem> items = mCurrentSlice.getItems();
-        SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
-                Slice.HINT_ACTIONS,
-                Slice.HINT_ALT);
-        String mode = getMode();
-        if (!mode.equals(mCurrentView.getMode())) {
-            removeAllViews();
-            mCurrentView = createView(mode);
-            addView(mCurrentView, getChildLp(mCurrentView));
-            addView(mActions, getChildLp(mActions));
-        }
-        if (mode.equals(MODE_LARGE)) {
-            ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
-        }
-        if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
-            mCurrentView.setVisibility(View.VISIBLE);
-            mCurrentView.setSlice(mCurrentSlice);
-        } else {
-            mCurrentView.setVisibility(View.GONE);
-        }
-
-        boolean showActions = mShowActions && actionRow != null
-                && !mode.equals(MODE_SHORTCUT);
-        if (showActions) {
-            mActions.setActions(actionRow, color);
-            mActions.setVisibility(View.VISIBLE);
-        } else {
-            mActions.setVisibility(View.GONE);
-        }
-    }
-
-    private LayoutParams getChildLp(View child) {
-        if (child instanceof ShortcutView) {
-            return new LayoutParams(mShortcutSize, mShortcutSize);
-        } else {
-            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        }
-    }
-
-    private static void validate(Uri sliceUri) {
-        if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
-            throw new RuntimeException("Invalid uri " + sliceUri);
-        }
-        if (sliceUri.getPathSegments().size() == 0) {
-            throw new RuntimeException("Invalid uri " + sliceUri);
-        }
-    }
-
-    private class SliceObserver extends ContentObserver {
-        SliceObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            this.onChange(selfChange, null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            Slice s = Slice.bindSlice(mContext.getContentResolver(), uri);
-            mCurrentSlice = s;
-            reinflate();
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/SliceViewUtil.java b/core/java/android/app/slice/widget/SliceViewUtil.java
deleted file mode 100644
index 1cf0055..0000000
--- a/core/java/android/app/slice/widget/SliceViewUtil.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.annotation.ColorInt;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * A bunch of utilities for slice UI.
- *
- * @hide
- */
-public class SliceViewUtil {
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorAccent(Context context) {
-        return getColorAttr(context, android.R.attr.colorAccent);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorError(Context context) {
-        return getColorAttr(context, android.R.attr.colorError);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getDefaultColor(Context context, int resId) {
-        final ColorStateList list = context.getResources().getColorStateList(resId,
-                context.getTheme());
-
-        return list.getDefaultColor();
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getDisabled(Context context, int inputColor) {
-        return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int applyAlphaAttr(Context context, int attr, int inputColor) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        float alpha = ta.getFloat(0, 0);
-        ta.recycle();
-        return applyAlpha(alpha, inputColor);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int applyAlpha(float alpha, int inputColor) {
-        alpha *= Color.alpha(inputColor);
-        return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
-                Color.blue(inputColor));
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorAttr(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        @ColorInt
-        int colorAccent = ta.getColor(0, 0);
-        ta.recycle();
-        return colorAccent;
-    }
-
-    /**
-     * @hide
-     */
-    public static int getThemeAttr(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        int theme = ta.getResourceId(0, 0);
-        ta.recycle();
-        return theme;
-    }
-
-    /**
-     * @hide
-     */
-    public static Drawable getDrawable(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        Drawable drawable = ta.getDrawable(0);
-        ta.recycle();
-        return drawable;
-    }
-
-    /**
-     * @hide
-     */
-    public static Icon createIconFromDrawable(Drawable d) {
-        if (d instanceof BitmapDrawable) {
-            return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
-        }
-        Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(b);
-        d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-        d.draw(canvas);
-        return Icon.createWithBitmap(b);
-    }
-
-    /**
-     * @hide
-     */
-    public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
-            boolean isLarge, ViewGroup parent) {
-        ImageView v = new ImageView(context);
-        v.setImageIcon(icon);
-        parent.addView(v);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
-        if (isLarge) {
-            // XXX better way to convert from icon -> bitmap or crop an icon (?)
-            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
-            Canvas iconCanvas = new Canvas(iconBm);
-            v.layout(0, 0, iconSize, iconSize);
-            v.draw(iconCanvas);
-            v.setImageBitmap(getCircularBitmap(iconBm));
-        } else {
-            v.setColorFilter(Color.WHITE);
-        }
-        lp.width = iconSize;
-        lp.height = iconSize;
-        lp.gravity = Gravity.CENTER;
-    }
-
-    /**
-     * @hide
-     */
-    public static Bitmap getCircularBitmap(Bitmap bitmap) {
-        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
-                bitmap.getHeight(), Config.ARGB_8888);
-        Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
-        paint.setAntiAlias(true);
-        canvas.drawARGB(0, 0, 0, 0);
-        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
-                bitmap.getWidth() / 2, paint);
-        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
-        canvas.drawBitmap(bitmap, rect, rect, paint);
-        return output;
-    }
-}
diff --git a/core/java/android/app/slice/widget/SmallTemplateView.java b/core/java/android/app/slice/widget/SmallTemplateView.java
deleted file mode 100644
index 1c4c5df..0000000
--- a/core/java/android/app/slice/widget/SmallTemplateView.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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 android.app.slice.widget;
-
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.text.Format;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Small template is also used to construct list items for use with {@link LargeTemplateView}.
- *
- * @hide
- */
-public class SmallTemplateView extends SliceModeView implements SliceListView {
-
-    private static final String TAG = "SmallTemplateView";
-
-    private int mIconSize;
-    private int mPadding;
-
-    private LinearLayout mStartContainer;
-    private TextView mTitleText;
-    private TextView mSecondaryText;
-    private LinearLayout mEndContainer;
-
-    public SmallTemplateView(Context context) {
-        super(context);
-        inflate(context, R.layout.slice_small_template, this);
-        mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
-        mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
-
-        mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
-        mTitleText = (TextView) findViewById(android.R.id.title);
-        mSecondaryText = (TextView) findViewById(android.R.id.summary);
-        mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_SMALL;
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        resetViews();
-        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        int color = colorItem != null ? colorItem.getColor() : -1;
-
-        // Look for any title elements
-        List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
-                null);
-        boolean hasTitleText = false;
-        boolean hasTitleItem = false;
-        for (int i = 0; i < titleItems.size(); i++) {
-            SliceItem item = titleItems.get(i);
-            if (!hasTitleItem) {
-                // icon, action icon, or timestamp
-                if (item.getType() == SliceItem.TYPE_ACTION) {
-                    hasTitleItem = addIcon(item, color, mStartContainer);
-                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
-                    addIcon(item, color, mStartContainer);
-                    hasTitleItem = true;
-                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
-                    TextView tv = new TextView(getContext());
-                    tv.setText(convertTimeToString(item.getTimestamp()));
-                    hasTitleItem = true;
-                }
-            }
-            if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
-                mTitleText.setText(item.getText());
-                hasTitleText = true;
-            }
-            if (hasTitleText && hasTitleItem) {
-                break;
-            }
-        }
-        mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
-        mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
-
-        if (slice.getType() != SliceItem.TYPE_SLICE) {
-            return;
-        }
-
-        // Deal with remaining items
-        int itemCount = 0;
-        boolean hasSummary = false;
-        ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
-                slice.getSlice().getItems());
-        for (int i = 0; i < sliceItems.size(); i++) {
-            SliceItem item = sliceItems.get(i);
-            if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
-                    && !item.hasHint(Slice.HINT_TITLE)) {
-                // TODO -- Should combine all text items?
-                mSecondaryText.setText(item.getText());
-                hasSummary = true;
-            }
-            if (itemCount <= 3) {
-                if (item.getType() == SliceItem.TYPE_ACTION) {
-                    if (addIcon(item, color, mEndContainer)) {
-                        itemCount++;
-                    }
-                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
-                    addIcon(item, color, mEndContainer);
-                    itemCount++;
-                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
-                    TextView tv = new TextView(getContext());
-                    tv.setText(convertTimeToString(item.getTimestamp()));
-                    mEndContainer.addView(tv);
-                    itemCount++;
-                } else if (item.getType() == SliceItem.TYPE_SLICE) {
-                    List<SliceItem> subItems = item.getSlice().getItems();
-                    for (int j = 0; j < subItems.size(); j++) {
-                        sliceItems.add(subItems.get(j));
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE,
-                slice.getHints().toArray(new String[slice.getHints().size()])));
-    }
-
-    /**
-     * @return Whether an icon was added.
-     */
-    private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
-        SliceItem image = null;
-        SliceItem action = null;
-        if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
-            image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
-            action = sliceItem;
-        } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
-            image = sliceItem;
-        }
-        if (image != null) {
-            ImageView iv = new ImageView(getContext());
-            iv.setImageIcon(image.getIcon());
-            if (action != null) {
-                final SliceItem sliceAction = action;
-                iv.setOnClickListener(v -> AsyncTask.execute(
-                        () -> {
-                            try {
-                                sliceAction.getAction().send();
-                            } catch (CanceledException e) {
-                                e.printStackTrace();
-                            }
-                        }));
-                iv.setBackground(SliceViewUtil.getDrawable(getContext(),
-                        android.R.attr.selectableItemBackground));
-            }
-            if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
-                iv.setColorFilter(color);
-            }
-            container.addView(iv);
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
-            lp.width = mIconSize;
-            lp.height = mIconSize;
-            lp.setMarginStart(mPadding);
-            return true;
-        }
-        return false;
-    }
-
-    private String convertTimeToString(long time) {
-        // TODO -- figure out what format(s) we support
-        Date date = new Date(time);
-        Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
-        return format.format(date);
-    }
-
-    private void resetViews() {
-        mStartContainer.removeAllViews();
-        mEndContainer.removeAllViews();
-        mTitleText.setText(null);
-        mSecondaryText.setText(null);
-    }
-}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index e845359..cd551bd 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -174,6 +174,11 @@
     public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
 
     /**
+     * Persist brightness slider events.
+     */
+    public abstract void persistBrightnessSliderEvents();
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 7fc7836..e1137aa 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -48,8 +48,7 @@
     private MemoryRegion[] mMemoryRegions;
 
     /*
-     * TODO(b/66965339): Deprecate this constructor and the setter methods, and mark private fields
-     * as final when the ContextHubService JNI code is removed.
+     * TODO(b/67734082): Deprecate this constructor and mark private fields as final.
      */
     public ContextHubInfo() {
     }
@@ -89,17 +88,6 @@
     }
 
     /**
-     * set the context hub unique identifer
-     *
-     * @param bytes - Maximum number of bytes per message
-     *
-     * @hide
-     */
-    public void setMaxPacketLenBytes(int bytes) {
-        mMaxPacketLengthBytes = bytes;
-    }
-
-    /**
      * get the context hub unique identifer
      *
      * @return int - unique system wide identifier
@@ -109,17 +97,6 @@
     }
 
     /**
-     * set the context hub unique identifer
-     *
-     * @param id - unique system wide identifier for the hub
-     *
-     * @hide
-     */
-    public void setId(int id) {
-        mId = id;
-    }
-
-    /**
      * get a string as a hub name
      *
      * @return String - a name for the hub
@@ -129,17 +106,6 @@
     }
 
     /**
-     * set a string as the hub name
-     *
-     * @param name - the name for the hub
-     *
-     * @hide
-     */
-    public void setName(String name) {
-        mName = name;
-    }
-
-    /**
      * get a string as the vendor name
      *
      * @return String - a name for the vendor
@@ -149,17 +115,6 @@
     }
 
     /**
-     * set a string as the vendor name
-     *
-     * @param vendor - a name for the vendor
-     *
-     * @hide
-     */
-    public void setVendor(String vendor) {
-        mVendor = vendor;
-    }
-
-    /**
      * get tool chain string
      *
      * @return String - description of the tool chain
@@ -169,17 +124,6 @@
     }
 
     /**
-     * set tool chain string
-     *
-     * @param toolchain - description of the tool chain
-     *
-     * @hide
-     */
-    public void setToolchain(String toolchain) {
-        mToolchain = toolchain;
-    }
-
-    /**
      * get platform version
      *
      * @return int - platform version number
@@ -189,17 +133,6 @@
     }
 
     /**
-     * set platform version
-     *
-     * @param platformVersion - platform version number
-     *
-     * @hide
-     */
-    public void setPlatformVersion(int platformVersion) {
-        mPlatformVersion = platformVersion;
-    }
-
-    /**
      * get static platform version number
      *
      * @return int - platform version number
@@ -209,15 +142,6 @@
     }
 
     /**
-     * set platform software version
-     *
-     * @param staticSwVersion - platform static s/w version number
-     *
-     * @hide
-     */
-    public void setStaticSwVersion(int staticSwVersion) {}
-
-    /**
      * get the tool chain version
      *
      * @return int - the tool chain version
@@ -227,17 +151,6 @@
     }
 
     /**
-     * set the tool chain version number
-     *
-     * @param toolchainVersion - tool chain version number
-     *
-     * @hide
-     */
-    public void setToolchainVersion(int toolchainVersion) {
-        mToolchainVersion = toolchainVersion;
-    }
-
-    /**
      * get the peak processing mips the hub can support
      *
      * @return float - peak MIPS that this hub can deliver
@@ -247,17 +160,6 @@
     }
 
     /**
-     * set the peak mips that this hub can support
-     *
-     * @param peakMips - peak mips this hub can deliver
-     *
-     * @hide
-     */
-    public void setPeakMips(float peakMips) {
-        mPeakMips = peakMips;
-    }
-
-    /**
      * get the stopped power draw in milliwatts
      * This assumes that the hub enter a stopped state - which is
      * different from the sleep state. Latencies on exiting the
@@ -271,17 +173,6 @@
     }
 
     /**
-     * Set the power consumed by the hub in stopped state
-     *
-     * @param stoppedPowerDrawMw - stopped power in milli watts
-     *
-     * @hide
-     */
-    public void setStoppedPowerDrawMw(float stoppedPowerDrawMw) {
-        mStoppedPowerDrawMw = stoppedPowerDrawMw;
-    }
-
-    /**
      * get the power draw of the hub in sleep mode. This assumes
      * that the hub supports a sleep mode in which the power draw is
      * lower than the power consumed when the hub is actively
@@ -297,17 +188,6 @@
     }
 
     /**
-     * Set the sleep power draw in milliwatts
-     *
-     * @param sleepPowerDrawMw - sleep power draw in milliwatts.
-     *
-     * @hide
-     */
-    public void setSleepPowerDrawMw(float sleepPowerDrawMw) {
-        mSleepPowerDrawMw = sleepPowerDrawMw;
-    }
-
-    /**
      * get the peak powe draw of the hub. This is the power consumed
      * by the hub at maximum load.
      *
@@ -318,18 +198,6 @@
     }
 
     /**
-     * set the peak power draw of the hub
-     *
-     * @param peakPowerDrawMw - peak power draw of the hub in
-     *                        milliwatts.
-     *
-     * @hide
-     */
-    public void setPeakPowerDrawMw(float peakPowerDrawMw) {
-        mPeakPowerDrawMw = peakPowerDrawMw;
-    }
-
-    /**
      * get the sensors supported by this hub
      *
      * @return int[] - all the supported sensors on this hub
@@ -352,30 +220,6 @@
     }
 
     /**
-     * set the supported sensors on this hub
-     *
-     * @param supportedSensors - supported sensors on this hub
-     *
-     * @hide
-     */
-    public void setSupportedSensors(int[] supportedSensors) {
-        mSupportedSensors = Arrays.copyOf(supportedSensors, supportedSensors.length);
-    }
-
-    /**
-     * set memory regions for this hub
-     *
-     * @param memoryRegions - memory regions information
-     *
-     * @see MemoryRegion
-     *
-     * @hide
-     */
-    public void setMemoryRegions(MemoryRegion[] memoryRegions) {
-        mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
-    }
-
-    /**
      * @return the CHRE platform ID as defined in chre/version.h
      *
      * TODO(b/67734082): Expose as public API
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 3aaeb50..18287fa 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -94,5 +94,17 @@
      */
     void setAnalogForced(boolean isForced);
 
+    /**
+     * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+     * @return Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    Map setParameters(in Map parameters);
+
+    /**
+     * @param keys Parameter keys to fetch
+     * @return Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    Map getParameters(in List<String> keys);
+
     boolean isAntennaConnected();
 }
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index 6ed171b..775e25c 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -30,4 +30,9 @@
     void onBackgroundScanAvailabilityChange(boolean isAvailable);
     void onBackgroundScanComplete();
     void onProgramListChanged();
+
+    /**
+     * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    void onParametersUpdated(in Map parameters);
 }
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6e8991a..e93fd5f 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -309,6 +309,58 @@
     public abstract void setAnalogForced(boolean isForced);
 
     /**
+     * Generic method for setting vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Framework does not make any assumptions on the keys or values, other than
+     * ones stated in VendorKeyValue documentation (a requirement of key
+     * prefixes).
+     *
+     * For each pair in the result map, the key will be one of the keys
+     * contained in the input (possibly with wildcards expanded), and the value
+     * will be a vendor-specific result status (such as "OK" or an error code).
+     * The implementation may choose to return an empty map, or only return
+     * a status for a subset of the provided inputs, at its discretion.
+     *
+     * Application and HAL must not use keys with unknown prefix. In particular,
+     * it must not place a key-value pair in results vector for unknown key from
+     * parameters vector - instead, an unknown key should simply be ignored.
+     * In other words, results vector may contain a subset of parameter keys
+     * (however, the framework doesn't enforce a strict subset - the only
+     * formal requirement is vendor domain prefix for keys).
+     *
+     * @param parameters Vendor-specific key-value pairs.
+     * @return Operation completion status for parameters being set.
+     * @hide FutureFeature
+     */
+    public abstract @NonNull Map<String, String>
+            setParameters(@NonNull Map<String, String> parameters);
+
+    /**
+     * Generic method for retrieving vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Framework does not cache set/get requests, so it's possible for
+     * getParameter to return a different value than previous setParameter call.
+     *
+     * The syntax and semantics of keys are up to the vendor (as long as prefix
+     * rules are obeyed). For instance, vendors may include some form of
+     * wildcard support. In such case, result vector may be of different size
+     * than requested keys vector. However, wildcards are not recognized by
+     * framework and they are passed as-is to the HAL implementation.
+     *
+     * Unknown keys must be ignored and not placed into results vector.
+     *
+     * @param keys Parameter keys to fetch.
+     * @return Vendor-specific key-value pairs.
+     * @hide FutureFeature
+     */
+    public abstract @NonNull Map<String, String>
+            getParameters(@NonNull List<String> keys);
+
+    /**
      * Get current antenna connection state for current configuration.
      * Only valid if a configuration has been applied.
      * @return {@code true} if the antenna is connected, {@code false} otherwise.
@@ -429,6 +481,22 @@
          * Use {@link RadioTuner#getProgramList(String)} to get an actual list.
          */
         public void onProgramListChanged() {}
+
+        /**
+         * Generic callback for passing updates to vendor-specific parameter values.
+         * The framework does not interpret the parameters, they are passed
+         * in an opaque manner between a vendor application and HAL.
+         *
+         * It's up to the HAL implementation if and how to implement this callback,
+         * as long as it obeys the prefix rule. In particular, only selected keys
+         * may be notified this way. However, setParameters must not trigger
+         * this callback, while an internal event can change parameters
+         * asynchronously.
+         *
+         * @param parameters Vendor-specific key-value pairs.
+         * @hide FutureFeature
+         */
+        public void onParametersUpdated(@NonNull Map<String, String> parameters) {}
     }
 
 }
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index b621969..864d17c 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -24,6 +24,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Implements the RadioTuner interface by forwarding calls to radio service.
@@ -251,6 +252,24 @@
     }
 
     @Override
+    public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+        try {
+            return mTuner.setParameters(Objects.requireNonNull(parameters));
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
+    public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+        try {
+            return mTuner.getParameters(Objects.requireNonNull(keys));
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public boolean isAntennaConnected() {
         try {
             return mTuner.isAntennaConnected();
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index ffd5b30f..a01f658 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -22,6 +22,8 @@
 import android.os.Looper;
 import android.util.Log;
 
+import java.util.Map;
+
 /**
  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
  */
@@ -94,4 +96,9 @@
     public void onProgramListChanged() {
         mHandler.post(() -> mCallback.onProgramListChanged());
     }
+
+    @Override
+    public void onParametersUpdated(Map parameters) {
+        mHandler.post(() -> mCallback.onParametersUpdated(parameters));
+    }
 }
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index 8f1a5c4..af9a73c 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -29,7 +29,7 @@
     public String iface;
     public int uid;
     public int ethertype;
-    public byte[] dstHwAddr;
+    public MacAddress dstHwAddr;
     public String srcIp;
     public String dstIp;
     public int ipNextHeader;
@@ -44,7 +44,7 @@
         j.add(iface);
         j.add("uid: " + Integer.toString(uid));
         j.add("eth=0x" + Integer.toHexString(ethertype));
-        j.add("dstHw=" + MacAddress.stringAddrFromByteAddr(dstHwAddr));
+        j.add("dstHw=" + dstHwAddr);
         if (ipNextHeader > 0) {
             j.add("ipNxtHdr=" + ipNextHeader);
             j.add("srcIp=" + srcIp);
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 1ba9777..23c1f20 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -16,7 +16,6 @@
 
 package android.net.metrics;
 
-import android.net.MacAddress;
 import android.os.Process;
 import android.os.SystemClock;
 import android.util.SparseIntArray;
@@ -80,7 +79,7 @@
                 break;
         }
 
-        switch (MacAddress.macAddressType(ev.dstHwAddr)) {
+        switch (ev.dstHwAddr.addressType()) {
             case UNICAST:
                 l2UnicastCount++;
                 break;
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index a6c3009..8e33b65 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,8 +16,6 @@
 
 package android.os;
 
-import android.os.IStatsCallbacks;
-
 /**
   * Binder interface to communicate with the statistics management service.
   * {@hide}
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
index 95d2f66..d77e41e 100644
--- a/core/java/android/service/autofill/InternalSanitizer.java
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -16,6 +16,7 @@
 package android.service.autofill;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcelable;
 import android.view.autofill.AutofillValue;
@@ -32,7 +33,11 @@
     /**
      * Sanitizes an {@link AutofillValue}.
      *
+     * @return sanitized value or {@code null} if value could not be sanitized (for example: didn't
+     * match regex, it's an invalid type, regex failed, etc).
+     *
      * @hide
      */
+    @Nullable
     public abstract AutofillValue sanitize(@NonNull AutofillValue value);
 }
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 9a1dcbb..bc4b3fc 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -613,6 +613,11 @@
          *         usernameId, passwordId);
          * </pre>
          *
+         * <p>The sanitizer can also be used as an alternative for a
+         * {@link #setValidator(Validator) validator}&mdashif any of the {@code ids} is a
+         * {@link #SaveInfo.Builder(int, AutofillId[]) required id} and the {@code sanitizer} fail
+         * for it, then the save UI is not shown.
+         *
          * @param sanitizer an implementation provided by the Android System.
          * @param ids id of fields whose value will be sanitized.
          * @return this builder.
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index 12e85b1..a3a98d8 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -19,6 +19,7 @@
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -62,24 +63,31 @@
     /** @hide */
     @Override
     @TestApi
+    @Nullable
     public AutofillValue sanitize(@NonNull AutofillValue value) {
         if (value == null) {
             Slog.w(TAG, "sanitize() called with null value");
             return null;
         }
-        if (!value.isText()) return value;
+        if (!value.isText()) {
+            if (sDebug) Slog.d(TAG, "sanitize() called with non-text value: " + value);
+            return null;
+        }
 
         final CharSequence text = value.getTextValue();
 
         try {
             final Matcher matcher = mRegex.matcher(text);
-            if (!matcher.matches()) return value;
+            if (!matcher.matches()) {
+                if (sDebug) Slog.d(TAG, "sanitize(): " + mRegex + " failed for " + value);
+                return null;
+            }
 
             final CharSequence sanitized = matcher.replaceAll(mSubst);
             return AutofillValue.forText(sanitized);
         } catch (Exception e) {
             Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
-            return value;
+            return null;
         }
     }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5641009..aa2f1c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -61,6 +61,8 @@
     private static native long nativeCreateTransaction();
     private static native long nativeGetNativeTransactionFinalizer();
     private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+    private static native void nativeMergeTransaction(long transactionObj,
+            long otherTransactionObj);
     private static native void nativeSetAnimationTransaction(long transactionObj);
 
     private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
@@ -654,6 +656,19 @@
         }
     }
 
+    /**
+     * Merge the supplied transaction in to the deprecated "global" transaction.
+     * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
+     * <p>
+     * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+     */
+    @Deprecated
+    public static void mergeToGlobalTransaction(Transaction t) {
+        synchronized(sGlobalTransaction) {
+            sGlobalTransaction.merge(t);
+        }
+    }
+
     /** end a transaction */
     public static void closeTransaction() {
         closeTransaction(false);
@@ -1368,7 +1383,7 @@
          * Sets the security of the surface.  Setting the flag is equivalent to creating the
          * Surface with the {@link #SECURE} flag.
          */
-        Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+        public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
             sc.checkNotReleased();
             if (isSecure) {
                 nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
@@ -1449,5 +1464,14 @@
             nativeSetAnimationTransaction(mNativeObject);
             return this;
         }
+
+        /**
+         * Merge the other transaction into this transaction, clearing the
+         * other transaction as if it had been applied.
+         */
+        public Transaction merge(Transaction other) {
+            nativeMergeTransaction(mNativeObject, other.mNativeObject);
+            return this;
+        }
     }
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 534335b..c192f5c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1663,14 +1663,6 @@
     void writeToProto(ProtoOutputStream proto, long fieldId);
 
     /**
-     * Returns whether a given window type can be magnified.
-     *
-     * @param windowType The window type.
-     * @return True if the window can be magnified.
-     */
-    public boolean canMagnifyWindow(int windowType);
-
-    /**
      * Returns whether a given window type is considered a top level one.
      * A top level window does not have a container, i.e. attached window,
      * or if it has a container it is laid out as a top-level window, not
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 4cf3461..63519a6 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,6 +34,8 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.O_MR1;
+
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
         this.mPackageInfo = packageInfo;
@@ -83,7 +85,7 @@
      * supported by the current framework version.
      */
     public static boolean hasCorrectTargetSdkVersion(PackageInfo packageInfo) {
-        return packageInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1;
+        return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
     }
 
     public UserInfo getUserInfo() {
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 203de9c..e493739 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -29,10 +29,10 @@
 /**
  * Manages settings state for a WebView. When a WebView is first created, it
  * obtains a set of default settings. These default settings will be returned
- * from any getter call. A WebSettings object obtained from
- * WebView.getSettings() is tied to the life of the WebView. If a WebView has
- * been destroyed, any method call on WebSettings will throw an
- * IllegalStateException.
+ * from any getter call. A {@code WebSettings} object obtained from
+ * {@link WebView#getSettings()} is tied to the life of the WebView. If a WebView has
+ * been destroyed, any method call on {@code WebSettings} will throw an
+ * {@link IllegalStateException}.
  */
 // This is an abstract base class: concrete WebViewProviders must
 // create a class derived from this, and return an instance of it in the
@@ -41,13 +41,13 @@
     /**
      * Enum for controlling the layout of html.
      * <ul>
-     *   <li>NORMAL means no rendering changes. This is the recommended choice for maximum
+     *   <li>{@code NORMAL} means no rendering changes. This is the recommended choice for maximum
      *       compatibility across different platforms and Android versions.</li>
-     *   <li>SINGLE_COLUMN moves all content into one column that is the width of the
+     *   <li>{@code SINGLE_COLUMN} moves all content into one column that is the width of the
      *       view.</li>
-     *   <li>NARROW_COLUMNS makes all columns no wider than the screen if possible. Only use
+     *   <li>{@code NARROW_COLUMNS} makes all columns no wider than the screen if possible. Only use
      *       this for API levels prior to {@link android.os.Build.VERSION_CODES#KITKAT}.</li>
-     *   <li>TEXT_AUTOSIZING boosts font size of paragraphs based on heuristics to make
+     *   <li>{@code TEXT_AUTOSIZING} boosts font size of paragraphs based on heuristics to make
      *       the text readable when viewing a wide-viewport layout in the overview mode.
      *       It is recommended to enable zoom support {@link #setSupportZoom} when
      *       using this mode. Supported from API level
@@ -98,9 +98,9 @@
     /**
      * Enum for specifying the WebView's desired density.
      * <ul>
-     *   <li>FAR makes 100% looking like in 240dpi</li>
-     *   <li>MEDIUM makes 100% looking like in 160dpi</li>
-     *   <li>CLOSE makes 100% looking like in 120dpi</li>
+     *   <li>{@code FAR} makes 100% looking like in 240dpi</li>
+     *   <li>{@code MEDIUM} makes 100% looking like in 160dpi</li>
+     *   <li>{@code CLOSE} makes 100% looking like in 120dpi</li>
      * </ul>
      */
     public enum ZoomDensity {
@@ -652,7 +652,7 @@
      * true, {@link WebChromeClient#onCreateWindow} must be implemented by the
      * host application. The default is {@code false}.
      *
-     * @param support whether to suport multiple windows
+     * @param support whether to support multiple windows
      */
     public abstract void setSupportMultipleWindows(boolean support);
 
@@ -665,7 +665,7 @@
     public abstract boolean supportMultipleWindows();
 
     /**
-     * Sets the underlying layout algorithm. This will cause a relayout of the
+     * Sets the underlying layout algorithm. This will cause a re-layout of the
      * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}.
      *
      * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value
@@ -1198,7 +1198,7 @@
 
     /**
      * Tells JavaScript to open windows automatically. This applies to the
-     * JavaScript function window.open(). The default is {@code false}.
+     * JavaScript function {@code window.open()}. The default is {@code false}.
      *
      * @param flag {@code true} if JavaScript can open windows automatically
      */
@@ -1208,7 +1208,7 @@
      * Gets whether JavaScript can open windows automatically.
      *
      * @return {@code true} if JavaScript can open windows automatically during
-     *         window.open()
+     *         {@code window.open()}
      * @see #setJavaScriptCanOpenWindowsAutomatically
      */
     public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e330916..a2c55b0 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -90,6 +90,31 @@
  * another process. The hierarchy is inflated from a layout resource
  * file, and this class provides some basic operations for modifying
  * the content of the inflated hierarchy.
+ *
+ * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
+ * <ul>
+ *   <li>{@link android.widget.AdapterViewFlipper}</li>
+ *   <li>{@link android.widget.FrameLayout}</li>
+ *   <li>{@link android.widget.GridLayout}</li>
+ *   <li>{@link android.widget.GridView}</li>
+ *   <li>{@link android.widget.LinearLayout}</li>
+ *   <li>{@link android.widget.ListView}</li>
+ *   <li>{@link android.widget.RelativeLayout}</li>
+ *   <li>{@link android.widget.StackView}</li>
+ *   <li>{@link android.widget.ViewFlipper}</li>
+ * </ul>
+ * <p>And the following widgets:</p>
+ * <ul>
+ *   <li>{@link android.widget.AnalogClock}</li>
+ *   <li>{@link android.widget.Button}</li>
+ *   <li>{@link android.widget.Chronometer}</li>
+ *   <li>{@link android.widget.ImageButton}</li>
+ *   <li>{@link android.widget.ImageView}</li>
+ *   <li>{@link android.widget.ProgressBar}</li>
+ *   <li>{@link android.widget.TextClock}</li>
+ *   <li>{@link android.widget.TextView}</li>
+ * </ul>
+ * <p>Descendants of these classes are not supported.</p>
  */
 public class RemoteViews implements Parcelable, Filter {
 
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 7abc76a..408a4e9 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -9556,7 +9556,7 @@
             if (vScroll == 0 && hScroll == 0) {
                 return false;
             }
-            mRecyclerView.scrollBy(hScroll, vScroll);
+            mRecyclerView.smoothScrollBy(hScroll, vScroll);
             return true;
         }
 
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 1a19a40..dd3e6f0 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,10 +44,10 @@
 
 struct NativeFamilyBuilder {
     NativeFamilyBuilder(uint32_t langId, int variant)
-        : langId(langId), variant(static_cast<minikin::FontVariant>(variant)),
+        : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
           allowUnsupportedFont(false) {}
     uint32_t langId;
-    minikin::FontVariant variant;
+    minikin::FontFamily::Variant variant;
     bool allowUnsupportedFont;
     std::vector<minikin::Font> fonts;
     std::vector<minikin::FontVariation> axes;
@@ -141,7 +141,7 @@
     }
 
     builder->fonts.push_back(minikin::Font(minikinFont,
-            minikin::FontStyle(weight, static_cast<minikin::FontSlant>(italic))));
+            minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
     builder->axes.clear();
     return true;
 }
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5f32d37..1e7f5f5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -581,7 +581,7 @@
         // restore the original settings.
         paint->setTextSkewX(saveSkewX);
         paint->setFakeBoldText(savefakeBold);
-        if (paint->getFontVariant() == minikin::FontVariant::ELEGANT) {
+        if (paint->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT) {
             SkScalar size = paint->getTextSize();
             metrics->fTop = -size * kElegantTop / 2048;
             metrics->fBottom = -size * kElegantBottom / 2048;
@@ -880,12 +880,13 @@
 
     static jboolean isElegantTextHeight(jlong paintHandle) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        return obj->getFontVariant() == minikin::FontVariant::ELEGANT;
+        return obj->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT;
     }
 
     static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        obj->setFontVariant(aa ? minikin::FontVariant::ELEGANT : minikin::FontVariant::DEFAULT);
+        obj->setFamilyVariant(
+                aa ? minikin::FontFamily::Variant::ELEGANT : minikin::FontFamily::Variant::DEFAULT);
     }
 
     static jfloat getTextSize(jlong paintHandle) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 3e4073f..d67c0b0 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -83,7 +83,7 @@
 
 static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
     Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
-    return face->fStyle.weight;
+    return face->fStyle.weight();
 }
 
 static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 4e88a83..d7300c4 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -36,7 +36,7 @@
 #include <hwui/MinikinUtils.h>
 #include <hwui/Paint.h>
 #include <minikin/FontCollection.h>
-#include <minikin/LineBreaker.h>
+#include <minikin/AndroidLineBreakerHelper.h>
 #include <minikin/MinikinFont.h>
 
 namespace android {
@@ -52,63 +52,6 @@
 static jclass gLineBreaks_class;
 static JLineBreaksID gLineBreaks_fieldID;
 
-class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
-    public:
-        JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
-                const std::vector<float>& indents, const std::vector<float>& leftPaddings,
-                const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
-            : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
-              mIndents(indents), mLeftPaddings(leftPaddings),
-              mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
-
-        float getLineWidth(size_t lineNo) override {
-            const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
-                    ? mFirstWidth : mRestWidth;
-            return width - get(mIndents, lineNo);
-        }
-
-        float getMinLineWidth() override {
-            // A simpler algorithm would have been simply looping until the larger of
-            // mFirstLineCount and mIndents.size()-mOffset, but that does unnecessary calculations
-            // when mFirstLineCount is large. Instead, we measure the first line, all the lines that
-            // have an indent, and the first line after firstWidth ends and restWidth starts.
-            float minWidth = std::min(getLineWidth(0), getLineWidth(mFirstLineCount));
-            for (size_t lineNo = 1; lineNo + mOffset < mIndents.size(); lineNo++) {
-                minWidth = std::min(minWidth, getLineWidth(lineNo));
-            }
-            return minWidth;
-        }
-
-        float getLeftPadding(size_t lineNo) override {
-            return get(mLeftPaddings, lineNo);
-        }
-
-        float getRightPadding(size_t lineNo) override {
-            return get(mRightPaddings, lineNo);
-        }
-
-    private:
-        float get(const std::vector<float>& vec, size_t lineNo) {
-            if (vec.empty()) {
-                return 0;
-            }
-            const size_t index = lineNo + mOffset;
-            if (index < vec.size()) {
-                return vec[index];
-            } else {
-                return vec.back();
-            }
-        }
-
-        const float mFirstWidth;
-        const int32_t mFirstLineCount;
-        const float mRestWidth;
-        const std::vector<float>& mIndents;
-        const std::vector<float>& mLeftPaddings;
-        const std::vector<float>& mRightPaddings;
-        const int32_t mOffset;
-};
-
 static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
     if (javaArray == nullptr) {
          return std::vector<float>();
@@ -118,109 +61,8 @@
     }
 }
 
-class Run {
-    public:
-        Run(int32_t start, int32_t end) : mRange(start, end) {}
-        virtual ~Run() {}
-
-        virtual void addTo(minikin::LineBreaker* lineBreaker) = 0;
-
-    protected:
-        minikin::Range mRange;
-
-    private:
-        // Forbid copy and assign.
-        Run(const Run&) = delete;
-        void operator=(const Run&) = delete;
-};
-
-class StyleRun : public Run {
-    public:
-        StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
-                std::shared_ptr<minikin::FontCollection>&& collection, bool isRtl)
-            : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
-              mIsRtl(isRtl) {}
-
-        void addTo(minikin::LineBreaker* lineBreaker) override {
-            lineBreaker->addStyleRun(&mPaint, mCollection, mRange, mIsRtl);
-        }
-
-    private:
-        minikin::MinikinPaint mPaint;
-        std::shared_ptr<minikin::FontCollection> mCollection;
-        const bool mIsRtl;
-};
-
-class Replacement : public Run {
-    public:
-        Replacement(int32_t start, int32_t end, float width, uint32_t localeListId)
-            : Run(start, end), mWidth(width), mLocaleListId(localeListId) {}
-
-        void addTo(minikin::LineBreaker* lineBreaker) override {
-            lineBreaker->addReplacement(mRange, mWidth, mLocaleListId);
-        }
-
-    private:
-        const float mWidth;
-        const uint32_t mLocaleListId;
-};
-
-class StaticLayoutNative {
-    public:
-        StaticLayoutNative(
-                minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency,
-                bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
-                std::vector<float>&& rightPaddings)
-            : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
-              mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
-              mRightPaddings(std::move(rightPaddings)) {}
-
-        void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
-                         std::shared_ptr<minikin::FontCollection> collection, bool isRtl) {
-            mRuns.emplace_back(std::make_unique<StyleRun>(
-                    start, end, std::move(paint), std::move(collection), isRtl));
-        }
-
-        void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
-            mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId));
-        }
-
-        // Only valid while this instance is alive.
-        inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
-                float firstWidth, int32_t firstLineCount, float restWidth,
-                int32_t indentsAndPaddingsOffset) {
-            return std::make_unique<JNILineBreakerLineWidth>(
-                firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings,
-                indentsAndPaddingsOffset);
-        }
-
-        void addRuns(minikin::LineBreaker* lineBreaker) {
-            for (const auto& run : mRuns) {
-                run->addTo(lineBreaker);
-            }
-        }
-
-        void clearRuns() {
-            mRuns.clear();
-        }
-
-        inline minikin::BreakStrategy getStrategy() const { return mStrategy; }
-        inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; }
-        inline bool isJustified() const { return mIsJustified; }
-
-    private:
-        const minikin::BreakStrategy mStrategy;
-        const minikin::HyphenationFrequency mFrequency;
-        const bool mIsJustified;
-        const std::vector<float> mIndents;
-        const std::vector<float> mLeftPaddings;
-        const std::vector<float> mRightPaddings;
-
-        std::vector<std::unique_ptr<Run>> mRuns;
-};
-
-static inline StaticLayoutNative* toNative(jlong ptr) {
-    return reinterpret_cast<StaticLayoutNative*>(ptr);
+static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
+    return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
 }
 
 // set text and set a number of parameters for creating a layout (width, tabstops, strategy,
@@ -228,7 +70,7 @@
 static jlong nInit(JNIEnv* env, jclass /* unused */,
         jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
         jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
-    return reinterpret_cast<jlong>(new StaticLayoutNative(
+    return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
             static_cast<minikin::BreakStrategy>(breakStrategy),
             static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
             isJustified,
@@ -291,7 +133,7 @@
         jintArray recycleFlags,
         jfloatArray charWidths) {
 
-    StaticLayoutNative* builder = toNative(nativePtr);
+    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
 
     ScopedCharArrayRO text(env, javaText);
 
@@ -327,7 +169,7 @@
 // Basically similar to Paint.getTextRunAdvances but with C++ interface
 // CriticalNative
 static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
-    StaticLayoutNative* builder = toNative(nativePtr);
+    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
     const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
     minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
@@ -337,7 +179,7 @@
 // CriticalNative
 static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
         jfloat width) {
-    StaticLayoutNative* builder = toNative(nativePtr);
+    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
     builder->addReplacementRun(start, end, width, paint->getMinikinLocaleListId());
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfeba83..bb1bfad 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -311,6 +311,14 @@
     transaction->apply(sync);
 }
 
+static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
+        jlong transactionObj, jlong otherTransactionObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    auto otherTransaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(
+            otherTransactionObj);
+    transaction->merge(std::move(*otherTransaction));
+}
+
 static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     transaction->setAnimationTransaction();
@@ -882,6 +890,8 @@
             (void*)nativeApplyTransaction },
     {"nativeGetNativeTransactionFinalizer", "()J",
             (void*)nativeGetNativeTransactionFinalizer },
+    {"nativeMergeTransaction", "(JJ)V",
+            (void*)nativeMergeTransaction },
     {"nativeSetAnimationTransaction", "(J)V",
             (void*)nativeSetAnimationTransaction },
     {"nativeSetLayer", "(JJI)V",
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
new file mode 100644
index 0000000..a8da0bf
--- /dev/null
+++ b/core/proto/android/os/cpufreq.proto
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuFreqProto";
+
+package android.os;
+
+// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
+message CpuFreq {
+
+    optional int32 jiffy_hz = 1; // obtain by system config.
+
+    repeated CpuFreqStats cpu_freqs = 2;
+}
+
+// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
+message CpuFreqStats {
+
+    optional string cpu_name = 1;
+
+    message TimeInState {
+        optional int32 state_khz = 1;  // cpu frequency
+        optional int64 time_jiffy = 2; // number of jiffies
+    }
+    repeated TimeInState times = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 6c5206c..ecdabcf 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
+import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -78,6 +79,10 @@
         (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
     ];
 
+    optional CpuFreq cpu_freq = 2004 [
+        (section).type = SECTION_FILE,
+        (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
+    ];
 
     // System Services
     optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 86103e4..feef5ce 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3958,6 +3958,10 @@
         <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
-    </application>
+
+        <service android:name="com.android.server.display.BrightnessIdleJob"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+</application>
 
 </manifest>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index dddd52b..cea9cd7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -28,7 +28,7 @@
     <string name="unknownName" msgid="6867811765370350269">"غير معروف"</string>
     <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"البريد الصوتي"</string>
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
-    <string name="mmiError" msgid="5154499457739052907">"‏حدثت مشكلة في الاتصال أو أن كود MMI غير صحيح."</string>
+    <string name="mmiError" msgid="5154499457739052907">"‏حدثت مشكلة في الاتصال أو أن رمز MMI غير صحيح."</string>
     <string name="mmiFdnError" msgid="5224398216385316471">"تم تقييد التشغيل لأرقام الاتصال الثابت فقط."</string>
     <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"يتعذر تغيير إعدادات إعادة توجيه المكالمات من هاتفك أثناء التجوال."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"تم تمكين الخدمة."</string>
@@ -43,7 +43,7 @@
     <string name="mismatchPin" msgid="609379054496863419">"أرقام التعريف الشخصية التي كتبتها غير مطابقة."</string>
     <string name="invalidPin" msgid="3850018445187475377">"ادخل رقم تعريف شخصي مكون من ٤ إلى ٨ أرقام."</string>
     <string name="invalidPuk" msgid="8761456210898036513">"‏اكتب رمز PUK مكونًا من ٨ أرقام أو أكثر."</string>
-    <string name="needPuk" msgid="919668385956251611">"‏شريحة SIM مؤمّنة بكود PUK. اكتب كود PUK لإلغاء تأمينها."</string>
+    <string name="needPuk" msgid="919668385956251611">"‏شريحة SIM مؤمّنة برمز PUK. اكتب رمز PUK لإلغاء تأمينها."</string>
     <string name="needPuk2" msgid="4526033371987193070">"‏اكتب PUK2 لإلغاء تأمين شريحة SIM."</string>
     <string name="enablePin" msgid="209412020907207950">"‏محاولة غير ناجحة، مكّن قفل SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
@@ -139,8 +139,8 @@
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
     <string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
-    <string name="fcComplete" msgid="3118848230966886575">"اكتمل كود الميزة."</string>
-    <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن كود الميزة غير صحيح."</string>
+    <string name="fcComplete" msgid="3118848230966886575">"اكتمل رمز الميزة."</string>
+    <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن رمز الميزة غير صحيح."</string>
     <string name="httpErrorOk" msgid="1191919378083472204">"حسنًا"</string>
     <string name="httpError" msgid="7956392511146698522">"حدث خطأ في الشبكة."</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"‏تعذر العثور على عنوان URL."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 05ab42d..658e951 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1169,7 +1169,7 @@
     <string name="sim_done_button" msgid="827949989369963775">"Terminé"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"Carte SIM ajoutée."</string>
     <string name="sim_added_message" msgid="6599945301141050216">"Redémarrez votre appareil pour accéder au réseau mobile."</string>
-    <string name="sim_restart_button" msgid="4722407842815232347">"Recommencer"</string>
+    <string name="sim_restart_button" msgid="4722407842815232347">"Redémarrer"</string>
     <string name="carrier_app_dialog_message" msgid="7066156088266319533">"Pour que la nouvelle carte SIM fonctionne correctement, vous devez installer et ouvrir une application fournie par votre fournisseur de services."</string>
     <string name="carrier_app_dialog_button" msgid="7900235513678617329">"TÉLÉCHARGER L\'APPLICATION"</string>
     <string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"PAS MAINTENANT"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cea220a..c866c4c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -172,7 +172,7 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Kifaa kinadhibitiwa"</string>
-    <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gonga ili upate maelezo zaidi."</string>
+    <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gusa ili upate maelezo zaidi."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Data iliyomo kwenye kifaa chako itafutwa"</string>
     <string name="factory_reset_message" msgid="7972496262232832457">"Huwezi kutumia programu ya msimamizi. Sasa data iliyo kwenye kifaa chako itafutwa.\n\nIkiwa una maswali yoyote, wasiliana na msimamizi wa shirika lako."</string>
     <string name="me" msgid="6545696007631404292">"Mimi"</string>
@@ -251,7 +251,7 @@
     <string name="notification_channel_foreground_service" msgid="3931987440602669158">"Programu zinazotumia betri"</string>
     <string name="foreground_service_app_in_background" msgid="1060198778219731292">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string>
     <string name="foreground_service_apps_in_background" msgid="7175032677643332242">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string>
-    <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+    <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
     <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
     <string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
@@ -807,7 +807,7 @@
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Bakia kwenye Ukurasa huu"</string>
     <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nJe, una uhakika unataka kutoka kwenye ukurasa huu?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string>
-    <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string>
+    <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gusa mara mbili ili kukuza ndani na nje."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string>
     <string name="setup_autofill" msgid="7103495070180590814">"Weka uwezo wa kujaza kiotomatiki"</string>
     <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -971,7 +971,7 @@
     <string name="undo" msgid="7905788502491742328">"Tendua"</string>
     <string name="redo" msgid="7759464876566803888">"Rejesha"</string>
     <string name="autofill" msgid="3035779615680565188">"Kujaza kiotomatiki"</string>
-    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Uchaguzi wa maandishi?"</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Maandishi yaliyoteuliwa"</string>
     <string name="addToDictionary" msgid="4352161534510057874">"Ongeza kwenye kamusi"</string>
     <string name="deleteText" msgid="6979668428458199034">"Futa"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mbinu ya uingizaji"</string>
@@ -986,7 +986,7 @@
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumiwa"</string>
-    <string name="app_running_notification_text" msgid="1197581823314971177">"Gonga ili upate maelezo zaidi au usitishe programu."</string>
+    <string name="app_running_notification_text" msgid="1197581823314971177">"Gusa ili upate maelezo zaidi au usitishe programu."</string>
     <string name="ok" msgid="5970060430562524910">"Sawa"</string>
     <string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
     <string name="yes" msgid="5362982303337969312">"Sawa"</string>
@@ -1062,7 +1062,7 @@
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Programu zinaanza"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Inamaliza kuwasha."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
-    <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gonga ili uende kwenye programu"</string>
+    <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gusa ili uende kwenye programu"</string>
     <string name="heavy_weight_switcher_title" msgid="7153167085403298169">"Badilisha programu?"</string>
     <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"Programmu nyingine tayari inaendeshwa na lazima hiyo ikomeshwe kabla ya kuanza nyingine mpya."</string>
     <string name="old_app_action" msgid="493129172238566282">"Rejea katika <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -1107,7 +1107,7 @@
     <string name="wifi_available_title_connecting" msgid="1557292688310330032">"Inaunganisha kwenye mtandao wa Wi‑Fi unaotumiwa na mtu yeyote"</string>
     <string name="wifi_available_title_connected" msgid="7542672851522241548">"Imeunganisha kwenye mtandao wa Wi-Fi"</string>
     <string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Imeshindwa kuunganisha kwenye mtandao wa Wi‑Fi"</string>
-    <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gonga ili uone mitandao yote"</string>
+    <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gusa ili uone mitandao yote"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Unganisha"</string>
     <string name="wifi_available_action_all_networks" msgid="1100098935861622985">"Mitandao Yote"</string>
     <string name="wifi_available_sign_in" msgid="9157196203958866662">"Ingia kwa mtandao wa Wi-Fi"</string>
@@ -1115,7 +1115,7 @@
     <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
     <skip />
     <string name="wifi_no_internet" msgid="8451173622563841546">"Wi-Fi haina muunganisho wa intaneti"</string>
-    <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gonga ili upate chaguo"</string>
+    <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="5325661434777870353">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina Intaneti. Huenda ukalipishwa."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1136,7 +1136,7 @@
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Haikuweza kuanzisha Wi-Fi Moja kwa Moja."</string>
     <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gonga ili uweke mipangilio"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gusa ili uweke mipangilio"</string>
     <string name="accept" msgid="1645267259272829559">"Kubali"</string>
     <string name="decline" msgid="2112225451706137894">"Kataa"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Mwaliko umetumwa"</string>
@@ -1172,7 +1172,7 @@
     <string name="carrier_app_dialog_button" msgid="7900235513678617329">"PATA PROGRAMU"</string>
     <string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"SIYO SASA"</string>
     <string name="carrier_app_notification_title" msgid="8921767385872554621">"SIM mpya imewekwa"</string>
-    <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gonga ili uiweke"</string>
+    <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gusa ili uiweke"</string>
     <string name="time_picker_dialog_title" msgid="8349362623068819295">"Weka saa"</string>
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Weka tarehe"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Weka"</string>
@@ -1188,11 +1188,11 @@
     <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB kwa ajili ya kuhamisha picha"</string>
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
-    <string name="usb_notification_message" msgid="3370903770828407960">"Gonga ili upate chaguo zaidi."</string>
+    <string name="usb_notification_message" msgid="3370903770828407960">"Gusa ili upate chaguo zaidi."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Imetambua kifaa cha sauti ya analogi"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gonga ili upate maelezo zaidi."</string>
+    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gusa ili upate maelezo zaidi."</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
-    <string name="adb_active_notification_message" msgid="4948470599328424059">"Gonga ili uzime utatuaji wa USB."</string>
+    <string name="adb_active_notification_message" msgid="4948470599328424059">"Gusa ili uzime utatuaji wa USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Inatayarisha ripoti ya hitilafu…"</string>
     <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Ungependa kushiriki ripoti ya hitilafu?"</string>
@@ -1204,23 +1204,23 @@
     <string name="show_ime" msgid="2506087537466597099">"Ionyeshe kwenye skrini wakati kibodi halisi inatumika"</string>
     <string name="hardware" msgid="194658061510127999">"Onyesha kibodi pepe"</string>
     <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Sanidi kibodi halisi"</string>
-    <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gonga ili uchague lugha na muundo"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gusa ili uchague lugha na muundo"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"Onyesha juu ya programu zingine"</string>
     <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine"</string>
     <string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine."</string>
-    <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gonga ili ufungue mipangilio na ukizime."</string>
+    <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gusa ili ufungue mipangilio na ukizime."</string>
     <string name="alert_windows_notification_turn_off_action" msgid="3367294525884949878">"ZIMA"</string>
     <string name="ext_media_checking_notification_title" msgid="5734005953288045806">"Inaandaa <xliff:g id="NAME">%s</xliff:g>"</string>
     <string name="ext_media_checking_notification_message" msgid="4747432538578886744">"Inakagua hitilafu"</string>
     <string name="ext_media_new_notification_message" msgid="7589986898808506239">"<xliff:g id="NAME">%s</xliff:g> mpya imegunduliwa"</string>
     <string name="ext_media_ready_notification_message" msgid="4083398150380114462">"Kwa ajili ya kuhamisha picha na maudhui"</string>
     <string name="ext_media_unmountable_notification_title" msgid="8295123366236989588">"<xliff:g id="NAME">%s</xliff:g> iliyoharibika"</string>
-    <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gonga ili uirekebishe."</string>
+    <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gusa ili uirekebishe."</string>
     <string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"<xliff:g id="NAME">%s</xliff:g> imeharibika. Ichague ili uirekebishe."</string>
     <string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g> isiyotumika"</string>
-    <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gonga ili uweke mipangilio ya muundo unaoweza kutumika."</string>
+    <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gusa ili uweke mipangilio ya muundo unaoweza kutumika."</string>
     <string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g> hii. Ichague ili uweke muundo unaotumika."</string>
     <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> imeondolewa bila kutarajiwa"</string>
     <string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"Ondoa <xliff:g id="NAME">%s</xliff:g> kabla ya kuchomoa ili kuepuka kupoteza data"</string>
@@ -1261,7 +1261,7 @@
     <string name="permdesc_requestDeletePackages" msgid="3406172963097595270">"Huruhusu programu kuomba idhini ya kufuta vifurushi."</string>
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"omba kupuuza uimarishji wa betri"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
-    <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gonga mara mbili kwa udhibiti wa kuza"</string>
+    <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gusa mara mbili kwa udhibiti wa kuza"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Nenda"</string>
     <string name="ime_action_search" msgid="658110271822807811">"Tafuta"</string>
@@ -1292,8 +1292,8 @@
     <string name="notification_ranker_binding_label" msgid="774540592299064747">"Huduma ya kupanga arifa"</string>
     <string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
     <string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="vpn_text" msgid="1610714069627824309">"Gonga ili kudhibiti mtandao."</string>
-    <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gonga ili kudhibiti mtandao"</string>
+    <string name="vpn_text" msgid="1610714069627824309">"Gusa ili kudhibiti mtandao."</string>
+    <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gusa ili kudhibiti mtandao"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kila mara VPN iliyowashwa inaunganishwa…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Kila mara VPN iliyowashwa imeunganishwa"</string>
     <string name="vpn_lockdown_disconnected" msgid="735805531187559719">"Imeondolewa kwenye VPN iliyowashwa kila wakati"</string>
@@ -1304,9 +1304,9 @@
     <string name="reset" msgid="2448168080964209908">"Weka upya"</string>
     <string name="submit" msgid="1602335572089911941">"Wasilisha"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mtindo wa gari umewezeshwa"</string>
-    <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gonga ili ufunge hali ya garini."</string>
+    <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gusa ili ufunge hali ya garini."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Gonga ili uweke mipangilio."</string>
+    <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
     <string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
     <string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
     <string name="back_button_label" msgid="2300470004503343439">"Nyuma"</string>
@@ -1383,7 +1383,7 @@
     <string name="storage_usb" msgid="3017954059538517278">"Hifadhi ya USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Badilisha"</string>
     <string name="data_usage_warning_title" msgid="3620440638180218181">"Tahadhari ya matumizi ya data"</string>
-    <string name="data_usage_warning_body" msgid="6660692274311972007">"Gonga ili uangalie matumizi na mipangilio."</string>
+    <string name="data_usage_warning_body" msgid="6660692274311972007">"Gusa ili uangalie matumizi na mipangilio."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Kikomo data ya 2G-3G kimefikiwa"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Kikomo cha data ya 4G kimefikiwa"</string>
     <string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"Umefikisha kipimo cha juu cha data"</string>
@@ -1395,7 +1395,7 @@
     <string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Taarifa za Wi-fi zimevuka kiwanga"</string>
     <string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> juu ya kikomo kilichobainishwa."</string>
     <string name="data_usage_restricted_title" msgid="5965157361036321914">"Data ya mandhari nyuma imezuiwa"</string>
-    <string name="data_usage_restricted_body" msgid="469866376337242726">"Gonga ili uondoe kizuizi."</string>
+    <string name="data_usage_restricted_body" msgid="469866376337242726">"Gusa ili uondoe kizuizi."</string>
     <string name="ssl_certificate" msgid="6510040486049237639">"Cheti cha usalama"</string>
     <string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Cheti hiki ni halali."</string>
     <string name="issued_to" msgid="454239480274921032">"Kimetolewa kwa:"</string>
@@ -1594,7 +1594,7 @@
     <string name="reason_unknown" msgid="6048913880184628119">"haijulikani"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"Huduma ya uchapishaji haijawashwa"</string>
     <string name="print_service_installed_title" msgid="2246317169444081628">"Huduma ya <xliff:g id="NAME">%s</xliff:g> imesakinisha"</string>
-    <string name="print_service_installed_message" msgid="5897362931070459152">"Gonga ili uwashe"</string>
+    <string name="print_service_installed_message" msgid="5897362931070459152">"Gusa ili uwashe"</string>
     <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Weka PIN ya msimamizi"</string>
     <string name="restr_pin_enter_pin" msgid="3395953421368476103">"Ingiza PIN"</string>
     <string name="restr_pin_incorrect" msgid="8571512003955077924">"Sio sahihi"</string>
@@ -1722,12 +1722,12 @@
     <string name="new_sms_notification_title" msgid="8442817549127555977">"Una ujumbe mpya"</string>
     <string name="new_sms_notification_content" msgid="7002938807812083463">"Fungua programu ya SMS ili uweze kuangalia"</string>
     <string name="user_encrypted_title" msgid="9054897468831672082">"Huenda baadhi ya utendaji ukawa vikwazo"</string>
-    <string name="user_encrypted_message" msgid="4923292604515744267">"Gonga ili ufungue"</string>
+    <string name="user_encrypted_message" msgid="4923292604515744267">"Gusa ili ufungue"</string>
     <string name="user_encrypted_detail" msgid="5708447464349420392">"Data ya mtumiaji imefungwa"</string>
     <string name="profile_encrypted_detail" msgid="3700965619978314974">"Wasifu wa kazini umefungwa"</string>
-    <string name="profile_encrypted_message" msgid="6964994232310195874">"Gonga ili ufungue wasifu wa kazini"</string>
+    <string name="profile_encrypted_message" msgid="6964994232310195874">"Gusa ili ufungue wasifu wa kazini"</string>
     <string name="usb_mtp_launch_notification_title" msgid="8359219638312208932">"Imeunganishwa na <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
-    <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gonga ili uangalie faili"</string>
+    <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gusa ili uangalie faili"</string>
     <string name="pin_target" msgid="3052256031352291362">"Bandika"</string>
     <string name="unpin_target" msgid="3556545602439143442">"Bandua"</string>
     <string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9e0722b..7a3fa1a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -152,7 +152,7 @@
     <!-- Displayed to tell the user that they should switch their network preference. -->
     <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
     <!-- Displayed to tell the user that they should switch their network preference. -->
-    <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; Internet &gt; Mobile networks &gt; Preferred network type."</string>
+    <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; internet &gt; Mobile networks &gt; Preferred network type."</string>
     <!-- Displayed to tell the user that emergency calls might not be available. -->
     <string name="EmergencyCallWarningTitle">Wi\u2011Fi calling is active</string>
     <!-- Displayed to tell the user that emergency calls might not be available. -->
@@ -3014,7 +3014,7 @@
     <string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
 
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's title. -->
-    <string name="wifi_no_internet">Wi-Fi has no Internet access</string>
+    <string name="wifi_no_internet">Wi-Fi has no internet access</string>
 
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
     <string name="wifi_no_internet_detailed">Tap for options</string>
@@ -3023,7 +3023,7 @@
     <string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
 
     <!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's message. %1$s is the network that the device switched to, e.g., cellular data. %2$s is the network type the device switched from, e.g., Wi-Fi. Both are strings in the network_switch_type_name array. -->
-    <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no Internet access. Charges may apply.</string>
+    <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no internet access. Charges may apply.</string>
 
     <!-- A toast might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the text of the toast. %1$s is the network that the device switched from, e.g., Wi-Fi. %2$s is the network type the device switched from, e.g., cellular data. Both are strings in the network_switch_type_name array. -->
     <string name="network_switch_metered_toast">Switched from <xliff:g id="previous_network">%1$s</xliff:g> to <xliff:g id="new_network">%2$s</xliff:g></string>
@@ -3043,7 +3043,7 @@
      <!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems.  This is the notification's title / ticker. -->
      <string name="wifi_watchdog_network_disabled">Couldn\'t connect to Wi-Fi</string>
      <!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems.  The complete alert msg is: <hotspot name> + this string, i.e. "Linksys has a poor internet connection" -->
-    <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor Internet connection.</string>
+    <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor internet connection.</string>
 
     <!-- Do not translate. Default access point SSID used for tethering -->
     <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 81164d5..76eb4e6 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -119,6 +119,22 @@
 CHECK_EMOJI := true
 endif
 
+fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp
+
 .PHONY: fontchain_lint
-fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
+fontchain_lint: $(fontchain_lint_timestamp)
+
+fontchain_lint_deps := \
+    external/unicode/DerivedAge.txt \
+    external/unicode/emoji-data.txt \
+    external/unicode/emoji-sequences.txt \
+    external/unicode/emoji-variation-sequences.txt \
+    external/unicode/emoji-zwj-sequences.txt \
+    external/unicode/additions/emoji-data.txt \
+    external/unicode/additions/emoji-sequences.txt \
+    external/unicode/additions/emoji-zwj-sequences.txt \
+
+$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)
+	@echo Running fontchain lint
 	$(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+	touch $@
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 209f364..dad24da 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -115,10 +115,14 @@
     <family lang="und-Hebr">
         <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
     </family>
     <family lang="und-Thai" variant="elegant">
         <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
     </family>
     <family lang="und-Thai" variant="compact">
         <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
@@ -127,14 +131,20 @@
     <family lang="und-Armn">
         <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.ttf</font>
     </family>
     <family lang="und-Geor und-Geok">
         <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Bold.ttf</font>
     </family>
     <family lang="und-Deva" variant="elegant">
         <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Bold.ttf</font>
     </family>
     <family lang="und-Deva" variant="compact">
         <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
@@ -147,6 +157,8 @@
     <family lang="und-Gujr" variant="elegant">
         <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Bold.ttf</font>
     </family>
     <family lang="und-Gujr" variant="compact">
         <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
@@ -163,6 +175,8 @@
     <family lang="und-Taml" variant="elegant">
         <font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Bold.ttf</font>
     </family>
     <family lang="und-Taml" variant="compact">
         <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
@@ -171,6 +185,8 @@
     <family lang="und-Mlym" variant="elegant">
         <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Bold.ttf</font>
     </family>
     <family lang="und-Mlym" variant="compact">
         <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
@@ -179,6 +195,8 @@
     <family lang="und-Beng" variant="elegant">
         <font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Bold.ttf</font>
     </family>
     <family lang="und-Beng" variant="compact">
         <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
@@ -187,6 +205,8 @@
     <family lang="und-Telu" variant="elegant">
         <font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Bold.ttf</font>
     </family>
     <family lang="und-Telu" variant="compact">
         <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
@@ -195,6 +215,8 @@
     <family lang="und-Knda" variant="elegant">
         <font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Bold.ttf</font>
     </family>
     <family lang="und-Knda" variant="compact">
         <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
@@ -258,6 +280,8 @@
     <family lang="und-Laoo" variant="elegant">
         <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
     </family>
     <family lang="und-Laoo" variant="compact">
         <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
@@ -472,15 +496,19 @@
     </family>
     <family lang="zh-Hans">
         <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
     </family>
     <family lang="zh-Hant zh-Bopo">
         <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
     </family>
     <family lang="ja">
         <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
     </family>
     <family lang="ko">
         <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
     </family>
     <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 90fe0b7..bad766c 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -29,12 +29,6 @@
 minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
                                                         const Typeface* typeface) {
     const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
-    minikin::FontStyle resolved = resolvedFace->fStyle;
-
-    const minikin::FontVariant minikinVariant =
-            (paint->getFontVariant() == minikin::FontVariant::ELEGANT)
-                    ? minikin::FontVariant::ELEGANT
-                    : minikin::FontVariant::COMPACT;
 
     minikin::MinikinPaint minikinPaint;
     /* Prepare minikin Paint */
@@ -46,7 +40,8 @@
     minikinPaint.wordSpacing = paint->getWordSpacing();
     minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
     minikinPaint.localeListId = paint->getMinikinLocaleListId();
-    minikinPaint.fontStyle = minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant);
+    minikinPaint.familyVariant = paint->getFamilyVariant();
+    minikinPaint.fontStyle = resolvedFace->fStyle;
     minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
     minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
     return minikinPaint;
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 76beb11..002f759 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -73,9 +73,9 @@
 
     uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
 
-    void setFontVariant(minikin::FontVariant variant) { mFontVariant = variant; }
+    void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; }
 
-    minikin::FontVariant getFontVariant() const { return mFontVariant; }
+    minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; }
 
     void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; }
 
@@ -90,7 +90,7 @@
     float mWordSpacing = 0;
     std::string mFontFeatureSettings;
     uint32_t mMinikinLocaleListId;
-    minikin::FontVariant mFontVariant;
+    minikin::FontFamily::Variant mFamilyVariant;
     uint32_t mHyphenEdit = 0;
     // The native Typeface object has the same lifetime of the Java Typeface
     // object. The Java Paint object holds a strong reference to the Java Typeface
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 94492c5..ae9c475 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -24,7 +24,7 @@
         , mWordSpacing(0)
         , mFontFeatureSettings()
         , mMinikinLocaleListId(0)
-        , mFontVariant(minikin::FontVariant::DEFAULT) {}
+        , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
 
 Paint::Paint(const Paint& paint)
         : SkPaint(paint)
@@ -32,7 +32,7 @@
         , mWordSpacing(paint.mWordSpacing)
         , mFontFeatureSettings(paint.mFontFeatureSettings)
         , mMinikinLocaleListId(paint.mMinikinLocaleListId)
-        , mFontVariant(paint.mFontVariant)
+        , mFamilyVariant(paint.mFamilyVariant)
         , mHyphenEdit(paint.mHyphenEdit)
         , mTypeface(paint.mTypeface) {}
 
@@ -42,7 +42,7 @@
         , mWordSpacing(0)
         , mFontFeatureSettings()
         , mMinikinLocaleListId(0)
-        , mFontVariant(minikin::FontVariant::DEFAULT) {}
+        , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
 
 Paint::~Paint() {}
 
@@ -52,7 +52,7 @@
     mWordSpacing = other.mWordSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
     mMinikinLocaleListId = other.mMinikinLocaleListId;
-    mFontVariant = other.mFontVariant;
+    mFamilyVariant = other.mFamilyVariant;
     mHyphenEdit = other.mHyphenEdit;
     mTypeface = other.mTypeface;
     return *this;
@@ -62,7 +62,8 @@
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
            a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing &&
            a.mFontFeatureSettings == b.mFontFeatureSettings &&
-           a.mMinikinLocaleListId == b.mMinikinLocaleListId && a.mFontVariant == b.mFontVariant &&
-           a.mHyphenEdit == b.mHyphenEdit && a.mTypeface == b.mTypeface;
+           a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
+           a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
+           a.mTypeface == b.mTypeface;
 }
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index e527adc..ebc14c8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -45,7 +45,7 @@
 
 static minikin::FontStyle computeMinikinStyle(int weight, bool italic) {
     return minikin::FontStyle(uirenderer::MathUtils::clamp(weight, 1, 1000),
-                              static_cast<minikin::FontSlant>(italic));
+                              static_cast<minikin::FontStyle::Slant>(italic));
 }
 
 // Resolve the relative weight from the baseWeight and target style.
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 1fcc028..66d6f52 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -81,39 +81,39 @@
 
 TEST(TypefaceTest, createWithDifferentBaseWeight) {
     std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
 
     std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
-    EXPECT_EQ(300, light->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, light->fStyle.slant);
+    EXPECT_EQ(300, light->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, light->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromRegular) {
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(Typeface::createRelative(nullptr, Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -123,30 +123,30 @@
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(700, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(700, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(1000, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(1000, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(700, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(700, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(1000, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(1000, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -156,30 +156,30 @@
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(300, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(300, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(600, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(600, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.ITLIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(300, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(300, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(600, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(600, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -189,22 +189,22 @@
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -212,8 +212,8 @@
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -224,23 +224,23 @@
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT,
     // Typeface.ITALIC), Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -248,8 +248,8 @@
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -261,8 +261,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java,
@@ -270,8 +270,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
@@ -279,8 +279,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -289,8 +289,8 @@
     // Typeface.create(typeface, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -300,8 +300,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false)
     //     .build();
     std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java,
@@ -309,8 +309,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
     //     .build();
     std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
@@ -318,8 +318,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -327,8 +327,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 
     // In Java,
@@ -336,8 +336,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
-    EXPECT_EQ(1000, over1000->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+    EXPECT_EQ(1000, over1000->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
@@ -346,24 +346,24 @@
     // Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
     std::unique_ptr<Typeface> regular(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new
     // Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
     std::unique_ptr<Typeface> bold(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new
     // Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
     std::unique_ptr<Typeface> italic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -371,8 +371,8 @@
     // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -380,8 +380,8 @@
     // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
     std::unique_ptr<Typeface> over1000(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
-    EXPECT_EQ(1000, over1000->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+    EXPECT_EQ(1000, over1000->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
@@ -389,30 +389,30 @@
     // In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
     std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
     std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
     std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
                                          RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 }
 
@@ -422,8 +422,8 @@
             buildFamily(kRobotoBoldItalic)};
     std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
             std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, typeface->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+    EXPECT_EQ(400, typeface->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
 }
 
 TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
@@ -431,8 +431,8 @@
             buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)};
     std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
             std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, typeface->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+    EXPECT_EQ(700, typeface->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
 }
 
 }  // namespace
diff --git a/packages/CarrierDefaultApp/res/values-sw/strings.xml b/packages/CarrierDefaultApp/res/values-sw/strings.xml
index c546fcee..a52a733 100644
--- a/packages/CarrierDefaultApp/res/values-sw/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sw/strings.xml
@@ -5,7 +5,7 @@
     <string name="android_system_label" msgid="2797790869522345065">"Mtoa Huduma za Simu"</string>
     <string name="portal_notification_id" msgid="5155057562457079297">"Data ya mtandao wa simu imekwisha"</string>
     <string name="no_data_notification_id" msgid="668400731803969521">"Data yako ya mtandao wa simu imezimwa"</string>
-    <string name="portal_notification_detail" msgid="2295729385924660881">"Gonga ili utembelee tovuti ya %s"</string>
+    <string name="portal_notification_detail" msgid="2295729385924660881">"Gusa ili utembelee tovuti ya %s"</string>
     <string name="no_data_notification_detail" msgid="3112125343857014825">"Tafadhali wasiliana na mtoa huduma wako %s"</string>
     <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Hakuna muunganisho wa data kwa simu za mkononi"</string>
     <string name="no_mobile_data_connection" msgid="544980465184147010">"Ongeza mpango wa mitandao mingine au data kupitia %s"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ec34253..e0a979d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
     <string name="wifi_security_none" msgid="7985461072596594400">"لا شيء"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"معطلة"</string>
-    <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏أخفقت تهيئة عنوان IP"</string>
+    <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏تعذّرت تهيئة عنوان IP"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string>
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏تعذّر اتصال WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index bb5d2d8..1b78d30 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordinateur"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Écouteurs"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Téléphone"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imagerie"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Écouteurs"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Périphérique d\'entrée"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé."</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté."</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi : une barre."</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec audio Bluetooth LDAC : qualité de lecture"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Sélectionner le codec audio Bluetooth LDAC :\nQualité de lecture"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Diffusion : <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS par TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"Si cette option est activée, essayez le DNS par TLS sur le port 853."</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 308e97d8..b66f95f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -48,7 +48,7 @@
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
     <string name="speed_label_medium" msgid="3175763313268941953">"普通"</string>
     <string name="speed_label_fast" msgid="7715732164050975057">"速い"</string>
-    <string name="speed_label_very_fast" msgid="2265363430784523409">"とても速い"</string>
+    <string name="speed_label_very_fast" msgid="2265363430784523409">"非常に速い"</string>
     <string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="6557104142667339895">"切断"</string>
     <string name="bluetooth_disconnecting" msgid="8913264760027764974">"切断中..."</string>
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PINまたはパスキーが正しくないため、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できませんでした。"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>と通信できません。"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ペア設定が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に拒否されました。"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"コンピュータ"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ヘッドセット"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"スマートフォン"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"画像"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ヘッドフォン"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"入力用周辺機器"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-FiはOFFです。"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fiが切断されました。"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fiはレベル1です。"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth オーディオ LDAC コーデック: 再生音質"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth オーディオ LDAC コーデックを選択:\n再生音質"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ストリーミング: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"この設定を有効にした場合、ポート 853 で DNS over TLS を試行します。"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"プライベート DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"プライベート DNS モードを選択"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"OFF"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"日和見"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"プライベート DNS プロバイダのホスト名"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ON にすると、Wi-Fi の電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4e3bc33..5a9bbc5 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN 또는 패스키가 잘못되어 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 통신할 수 없습니다."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 페어링을 거부했습니다."</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"컴퓨터"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"헤드셋"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"전화"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"이미징"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"헤드폰"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"입력 주변기기"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"블루투스"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi가 꺼져 있습니다."</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 연결이 끊어졌습니다."</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 신호 막대가 한 개입니다."</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"블루투스 오디오 LDAC 코덱: 재생 품질"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"블루투스 오디오 LDAC 코덱 선택:\n재생 품질"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"스트리밍: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"TLS를 통한 DNS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"사용 설정한 경우, 포트 853에서 TLS를 통해 DNS를 시도합니다."</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"비공개 DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"비공개 DNS 모드 선택"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"사용 안함"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"기회주의적"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"비공개 DNS 제공업체 호스트 이름"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시합니다."</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"사용 설정하면 Wi-Fi 신호가 약할 때 데이터 연결을 Wi-Fi에서 모바일 네트워크로 더욱 적극적으로 핸드오버합니다."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index a1beef6..2b31bf1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -316,7 +316,7 @@
     <string name="enable_freeform_support_summary" msgid="8247310463288834487">"Ruhusu uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
-    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gonga ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
+    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nenosiri jipya la hifadhi rudufu limewekwa"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
@@ -331,8 +331,8 @@
     <item msgid="5363960654009010371">"Rangi zilizoboreshwa kwa ajili ya maudhui dijitali"</item>
   </string-array>
     <string name="inactive_apps_title" msgid="1317817863508274533">"Programu zilizozimwa"</string>
-    <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gonga ili ugeuze."</string>
-    <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gonga ili ugeuze."</string>
+    <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gusa ili ugeuze."</string>
+    <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gusa ili ugeuze."</string>
     <string name="runningservices_settings_title" msgid="8097287939865165213">"Huduma zinazoendeshwa"</string>
     <string name="runningservices_settings_summary" msgid="854608995821032748">"Onyesha na dhibiti huduma zinazoendeshwa kwa sasa"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5ff39b6..2db6178 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ได้เพราะ PIN หรือรหัสผ่านไม่ถูกต้อง"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ไม่สามารถเชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ปฏิเสธการจับคู่อุปกรณ์"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"คอมพิวเตอร์"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ชุดหูฟัง"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"โทรศัพท์"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"การถ่ายภาพ"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"หูฟัง"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"อุปกรณ์อินพุต"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"บลูทูธ"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ปิดอยู่"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"สัญญาณ Wi-Fi 1 ขีด"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"เลือกตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC:\nคุณภาพการเล่น"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"สตรีมมิง: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS ผ่าน TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"หากเปิดใช้แล้ว ให้ลองใช้ DNS ผ่าน TLS บนพอร์ต 853"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ส่วนตัว"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"เลือกโหมด DNS ส่วนตัว"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"ปิด"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ที่ใช้โอกาส"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"ชื่อโฮสต์ของผู้ให้บริการ DNS ส่วนตัว"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือเมื่อสัญญาณ Wi-Fi อ่อน"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2554388..2215fe5 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"電腦"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"免持耳機"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"電話"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"顯像裝置"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"頭戴式耳機"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"周邊輸入裝置"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"藍牙"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號強度一格。"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"藍牙音訊 LDAC 轉碼器:播放品質"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"選取藍牙音訊 LDAC 轉碼器:\n播放品質"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"串流中:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"透過傳輸層安全標準 (TLS) 執行 DNS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"如果啟用這個選項,即可在通訊埠 853 上嘗試透過傳輸層安全標準 (TLS) 執行 DNS。"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人 DNS 模式"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"停用"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 26fd750..d82db41 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -77,7 +77,7 @@
     <!-- Summary for the network but no internet connection was detected. -->
     <string name="wifi_no_internet_no_reconnect">Won\'t automatically connect</string>
     <!-- Summary for the remembered network but no internet connection was detected. -->
-    <string name="wifi_no_internet">No Internet access</string>
+    <string name="wifi_no_internet">No internet access</string>
     <!-- Summary for saved networks -->
     <string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
 
@@ -95,7 +95,7 @@
     <string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
 
     <!-- Summary for Connected wifi network without internet -->
-    <string name="wifi_connected_no_internet">Connected, no Internet</string>
+    <string name="wifi_connected_no_internet">Connected, no internet</string>
 
     <!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
     <string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
@@ -192,14 +192,14 @@
     <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
     <string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (user role). [CHAR LIMIT=25]-->
-    <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for Internet access</string>
+    <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for internet access</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (NAP role). [CHAR LIMIT=25]-->
-    <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local Internet connection with device</string>
+    <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local internet connection with device</string>
 
     <!-- Bluetooth settings. Connection options screen. The summary
          for the PAN checkbox preference that describes how checking it
          will set the PAN profile as preferred. -->
-    <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
+    <string name="bluetooth_pan_profile_summary_use_for">Use for internet access</string>
     <!-- Bluetooth settings. Connection options screen.  The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
     <string name="bluetooth_map_profile_summary_use_for">Use for map</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. -->
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 3f3dd2a..e4dcbee 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -24,10 +24,10 @@
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"Tafadhali subiri…"</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tutatuma ripoti ya hitilafu kwenye simu baada ya muda mfupi"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Chagua kushiriki ripoti ya hitilafu"</string>
-    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gonga ili ushiriki ripoti yako ya hitilafu"</string>
+    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Chagua kushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
     <string name="bugreport_confirm" msgid="5917407234515812495">"Ripoti za hitilafu zinajumuisha data kutoka faili za kumbukumbu mbalimbali zilizo kwenye mfumo, ambazo huenda zinajumuisha data ambayo unachukulia kuwa nyeti (kama vile matumizi ya programu na maelezo kuhusu data ilipo). Shiriki ripoti za hitilafu na watu na programu unazoamini pekee."</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Usionyeshe tena"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 04ce752..839fb87 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -117,8 +117,8 @@
       <item quantity="other">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
       <item quantity="one">‏رمز PUK لشريحة SIM غير صالح، ويتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>)، تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
     </plurals>
-    <string name="kg_password_pin_failed" msgid="8769990811451236223">"‏أخفقت عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
-    <string name="kg_password_puk_failed" msgid="1331621440873439974">"‏أخفقت عملية PUK لشريحة SIM"</string>
+    <string name="kg_password_pin_failed" msgid="8769990811451236223">"‏تعذّر إتمام عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
+    <string name="kg_password_puk_failed" msgid="1331621440873439974">"‏تعذّر إتمام عملية PUK لشريحة SIM"</string>
     <string name="kg_pin_accepted" msgid="7637293533973802143">"تم قبول الرمز"</string>
     <string name="keyguard_carrier_default" msgid="4274828292998453695">"لا تتوفر خدمة."</string>
     <string name="accessibility_ime_switch_button" msgid="2695096475319405612">"تبديل أسلوب الإدخال"</string>
diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ar/config.xml
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -22,5 +22,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
     <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b5ac31a..d642118 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -55,15 +55,15 @@
     <string name="bluetooth_tethered" msgid="7094101612161133267">"تم إنشاء الاتصال بالإنترنت عن طريق البلوتوث."</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"إعداد أسلوب الإدخال"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"لوحة مفاتيح فعلية"</string>
-    <string name="usb_device_permission_prompt" msgid="834698001271562057">"‏هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى جهاز USB؟"</string>
-    <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"‏هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى ملحق USB؟"</string>
-    <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"‏هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل جهاز USB هذا؟"</string>
-    <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"‏هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل ملحق USB هذا؟"</string>
+    <string name="usb_device_permission_prompt" msgid="1825685909587559679">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+    <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
+    <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"هل تريد فتح <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+    <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"هل تريد فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
     <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏لا يعمل أي تطبيق مثبت مع ملحق UEB هذا. مزيد من المعلومات عن هذا الملحق على <xliff:g id="URL">%1$s</xliff:g>."</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"‏ملحق USB"</string>
     <string name="label_view" msgid="6304565553218192990">"عرض"</string>
-    <string name="always_use_device" msgid="1450287437017315906">"‏الاستخدام بشكل افتراضي لجهاز USB هذا"</string>
-    <string name="always_use_accessory" msgid="1210954576979621596">"‏الاستخدام بشكل افتراضي لملحق USB هذا"</string>
+    <string name="always_use_device" msgid="4015357883336738417">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
+    <string name="always_use_accessory" msgid="3257892669444535154">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
     <string name="usb_debugging_title" msgid="4513918393387141949">"‏هل تريد السماح بتصحيح أخطاء USB؟"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"‏الملف المرجعي الرئيسي لـ RSA في هذا الكمبيوتر هو:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"السماح دائمًا من هذا الكمبيوتر"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 989f6d1..6e1feea 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -51,21 +51,15 @@
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Connexion Bluetooth partagée"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
-    <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
-    <skip />
-    <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
-    <skip />
-    <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
-    <skip />
-    <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
-    <skip />
+    <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+    <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+    <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+    <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
     <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aucune application installée compatible avec accessoire USB. En savoir plus sur <xliff:g id="URL">%1$s</xliff:g>"</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"Accessoire USB"</string>
     <string name="label_view" msgid="6304565553218192990">"Afficher"</string>
-    <!-- no translation found for always_use_device (4015357883336738417) -->
-    <skip />
-    <!-- no translation found for always_use_accessory (3257892669444535154) -->
-    <skip />
+    <string name="always_use_device" msgid="4015357883336738417">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_DEVICE">%2$s</xliff:g> est connecté"</string>
+    <string name="always_use_accessory" msgid="3257892669444535154">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> est connecté"</string>
     <string name="usb_debugging_title" msgid="4513918393387141949">"Autoriser le débogage USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"Empreinte numérique de la clé RSA de l\'ordinateur : \n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Toujours autoriser sur cet ordinateur"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d1bbe50..5853d93 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -51,21 +51,15 @@
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth imefungwa"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Weka mbinu za ingizo"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Kibodi halisi"</string>
-    <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
-    <skip />
-    <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
-    <skip />
-    <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
-    <skip />
-    <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
-    <skip />
+    <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+    <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+    <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+    <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
     <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Hakuna programu zilizosakinishwa zinazofanya kazi na kifaa hiki cha USB. Pata maelezo zaidi kuhusu kifaa hiki kwenye <xliff:g id="URL">%1$s</xliff:g>"</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"Kifaa cha Usb"</string>
     <string name="label_view" msgid="6304565553218192990">"Ona"</string>
-    <!-- no translation found for always_use_device (4015357883336738417) -->
-    <skip />
-    <!-- no translation found for always_use_accessory (3257892669444535154) -->
-    <skip />
+    <string name="always_use_device" msgid="4015357883336738417">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_DEVICE">%2$s</xliff:g> inaunganishwa"</string>
+    <string name="always_use_accessory" msgid="3257892669444535154">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> inaunganishwa"</string>
     <string name="usb_debugging_title" msgid="4513918393387141949">"Ruhusu utatuaji wa USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"Alama ya kidole ya kitufe cha RSA ya kompyuta ni:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Ruhusu kutoka kwenye kompyuta hii kila wakati"</string>
@@ -77,7 +71,7 @@
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Inahifadhi picha ya skrini..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"Picha ya skrini inahifadhiwa"</string>
     <string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
-    <string name="screenshot_saved_text" msgid="2685605830386712477">"Gonga ili utazame picha ya skrini uliyohifadhi."</string>
+    <string name="screenshot_saved_text" msgid="2685605830386712477">"Gusa ili utazame picha ya skrini uliyohifadhi."</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini."</string>
     <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string>
@@ -363,7 +357,7 @@
     <string name="zen_silence_introduction" msgid="3137882381093271568">"Hatua hii huzuia sauti na mitetemo YOTE, ikiwa ni pamoja na ile inayotokana na kengele, muziki, video na michezo."</string>
     <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>+"</string>
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
-    <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
+    <string name="notification_tap_again" msgid="7590196980943943842">"Gusa tena ili ufungue"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
     <string name="do_disclosure_generic" msgid="5615898451805157556">"Kifaa hiki kinasimamiwa na shirika lako"</string>
     <string name="do_disclosure_with_name" msgid="5640615509915445501">"Kifaa hiki kinadhibitiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
@@ -500,11 +494,11 @@
     <string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="2447177903892477915">"Masafa ya ishara ya kampuni ya simu"</string>
     <string name="stream_accessibility" msgid="301136219144385106">"Zana za walio na matatizo ya kuona au kusikia"</string>
-    <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gonga ili urejeshe."</string>
-    <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gonga ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
-    <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gonga ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
-    <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gonga ili uweke mtetemo."</string>
-    <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gonga ili usitishe."</string>
+    <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gusa ili urejeshe."</string>
+    <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+    <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+    <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gusa ili uweke mtetemo."</string>
+    <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gusa ili usitishe."</string>
     <string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Inaonyesha %s ya vidhibiti vya sauti. Telezesha kidole juu ili uondoe."</string>
     <string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Imeficha vidhibiti vya sauti"</string>
     <string name="system_ui_tuner" msgid="708224127392452018">"Kirekebishi cha kiolesura cha mfumo"</string>
@@ -700,9 +694,9 @@
     <string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"Juu 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"Juu 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Skrini nzima ya chini"</string>
-    <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gonga mara mbili ili ubadilishe."</string>
-    <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gonga mara mbili ili uongeze."</string>
-    <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gonga mara mbili ili uchague."</string>
+    <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string>
+    <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string>
+    <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gusa mara mbili ili uchague."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imeongezwa kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
@@ -776,7 +770,7 @@
     <string name="qs_dnd_keep" msgid="1825009164681928736">"Usibadilishe"</string>
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Badilisha"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"Programu zinatumika chinichini"</string>
-    <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+    <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
     <string name="data_usage_disable_mobile" msgid="5116269981510015864">"Ungependa kuzima data ya mtandao wa simu?"</string>
     <string name="touch_filtered_warning" msgid="8671693809204767551">"Kwa sababu programu nyingine inazuia ombi la ruhusa, hatuwezi kuthibitisha jibu lako katika Mipangilio."</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c18f9b6..cb3d59c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,6 +33,8 @@
 import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 
+import java.util.Collections;
+
 /**
  * View visible under the clock on the lock screen and AoD.
  */
@@ -75,7 +77,8 @@
         super.onAttachedToWindow();
 
         // Set initial content
-        showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+        showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+                Collections.emptyList()));
 
         // Make sure we always have the most current slice
         getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri,
@@ -91,7 +94,7 @@
 
     private void showSlice(Slice slice) {
         // Items will be wrapped into an action when they have tap targets.
-        SliceItem actionSlice = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
+        SliceItem actionSlice = SliceQuery.find(slice, SliceItem.FORMAT_ACTION);
         if (actionSlice != null) {
             mSlice = actionSlice.getSlice();
             mSliceAction = actionSlice.getAction();
@@ -105,7 +108,7 @@
             return;
         }
 
-        SliceItem title = SliceQuery.find(mSlice, SliceItem.TYPE_TEXT, Slice.HINT_TITLE, null);
+        SliceItem title = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE, null);
         if (title == null) {
             mTitle.setVisibility(GONE);
         } else {
@@ -113,7 +116,7 @@
             mTitle.setText(title.getText());
         }
 
-        SliceItem text = SliceQuery.find(mSlice, SliceItem.TYPE_TEXT, null, Slice.HINT_TITLE);
+        SliceItem text = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, null, Slice.HINT_TITLE);
         if (text == null) {
             mText.setVisibility(GONE);
         } else {
@@ -154,7 +157,8 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+            showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+                    Collections.emptyList()));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 03018f7..6ddc76b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -84,7 +84,7 @@
 
     @Override
     public Slice onBindSlice(Uri sliceUri) {
-        return new Slice.Builder(sliceUri).addText(mLastText, Slice.HINT_TITLE).build();
+        return new Slice.Builder(sliceUri).addText(mLastText, null, Slice.HINT_TITLE).build();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index b585bdf..f451fda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
@@ -75,17 +76,20 @@
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private final OnSettingsClickListener mOnSettingsClickListener;
     private String mKeyToRemoveOnGutsClosed;
 
     public NotificationGutsManager(
             NotificationPresenter presenter,
             NotificationStackScrollLayout stackScroller,
             NotificationInfo.CheckSaveListener checkSaveListener,
-            Context context) {
+            Context context,
+            OnSettingsClickListener onSettingsClickListener) {
         mPresenter = presenter;
         mStackScroller = stackScroller;
         mCheckSaveListener = checkSaveListener;
         mContext = context;
+        mOnSettingsClickListener = onSettingsClickListener;
         Resources res = context.getResources();
 
         mNonBlockablePkgs = new HashSet<>();
@@ -189,6 +193,7 @@
                 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
                     mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
                     guts.resetFalsingCheck();
+                    mOnSettingsClickListener.onClick(sbn.getKey());
                     startAppNotificationSettingsActivity(pkg, appUid, channel);
                 };
             }
@@ -352,4 +357,8 @@
         pw.print("mKeyToRemoveOnGutsClosed: ");
         pw.println(mKeyToRemoveOnGutsClosed);
     }
+
+    public interface OnSettingsClickListener {
+        void onClick(String key);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6775615..1bbb94a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -43,6 +43,7 @@
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -919,7 +920,14 @@
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
         mGutsManager = new NotificationGutsManager(this, mStackScroller,
-                mCheckSaveListener, mContext);
+                mCheckSaveListener, mContext,
+                key -> {
+                    try {
+                        mBarService.onNotificationSettingsViewed(key);
+                    } catch (RemoteException e) {
+                        // if we're here we're dead
+                    }
+                });
         mNotificationPanel.setStatusBar(this);
         mNotificationPanel.setGroupManager(mGroupManager);
         mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1470,6 +1478,11 @@
                         }
                     }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
                 }
+                try {
+                    mBarService.onNotificationDirectReplied(entry.key);
+                } catch (RemoteException e) {
+                    // system process is dead if we're here.
+                }
             }
         });
 
@@ -1785,9 +1798,14 @@
         final int id = n.getId();
         final int userId = n.getUserId();
         try {
-            // TODO: record actual dismissal surface
+            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+            if (isHeadsUp(n.getKey())) {
+                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+            } else if (mStackScroller.hasPulsingNotifications()) {
+                dismissalSurface = NotificationStats.DISMISSAL_AOD;
+            }
             mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
-                    NotificationStats.DISMISSAL_OTHER);
+                    dismissalSurface);
             if (FORCE_REMOTE_INPUT_HISTORY
                     && mKeysKeptForRemoteInput.contains(n.getKey())) {
                 mKeysKeptForRemoteInput.remove(n.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4b8f581..0d41e20 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -207,6 +207,7 @@
         } else {
             addExistingRows();
         }
+        updateRowsH(getActiveRow());
     }
 
     private ColorStateList loadColorStateList(int colorResId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 4437aac..4eae342 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -63,7 +63,7 @@
     @Test
     public void returnsValidSlice() {
         Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI));
-        SliceItem text = SliceQuery.find(slice, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
+        SliceItem text = SliceQuery.find(slice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE,
                 null /* nonHints */);
         Assert.assertNotNull("Slice must provide a title.", text);
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index af4668a..9741486 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1167,7 +1167,16 @@
                         break;
                     }
                 }
+
                 value = getSanitizedValue(sanitizers, id, value);
+                if (value == null) {
+                    if (sDebug) {
+                        Slog.d(TAG, "value of required field " + id + " failed sanitization");
+                    }
+                    allRequiredAreNotEmpty = false;
+                    break;
+                }
+                viewState.setSanitizedValue(value);
                 currentValues.put(id, value);
                 final AutofillValue filledValue = viewState.getAutofilledValue();
 
@@ -1337,7 +1346,7 @@
         return sanitizers;
     }
 
-    @NonNull
+    @Nullable
     private AutofillValue getSanitizedValue(
             @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
             @NonNull AutofillId id,
@@ -1431,10 +1440,10 @@
             if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + context);
 
             for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
-                final ViewState state = mViewStates.valueAt(viewStateNum);
+                final ViewState viewState = mViewStates.valueAt(viewStateNum);
 
-                final AutofillId id = state.id;
-                final AutofillValue value = state.getCurrentValue();
+                final AutofillId id = viewState.id;
+                final AutofillValue value = viewState.getCurrentValue();
                 if (value == null) {
                     if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + id);
                     continue;
@@ -1446,9 +1455,17 @@
                 }
                 if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
 
-                final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+                AutofillValue sanitizedValue = viewState.getSanitizedValue();
 
-                node.updateAutofillValue(sanitizedValue);
+                if (sanitizedValue == null) {
+                    // Field is optional and haven't been sanitized yet.
+                    sanitizedValue = getSanitizedValue(sanitizers, id, value);
+                }
+                if (sanitizedValue != null) {
+                    node.updateAutofillValue(sanitizedValue);
+                } else if (sDebug) {
+                    Slog.d(TAG, "Not updating field " + id + " because it failed sanitization");
+                }
             }
 
             // Sanitize structure before it's sent to service.
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 832a66b..0dbdc13 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -76,6 +76,7 @@
     private FillResponse mResponse;
     private AutofillValue mCurrentValue;
     private AutofillValue mAutofilledValue;
+    private AutofillValue mSanitizedValue;
     private Rect mVirtualBounds;
     private int mState;
     private String mDatasetId;
@@ -117,6 +118,15 @@
     }
 
     @Nullable
+    AutofillValue getSanitizedValue() {
+        return mSanitizedValue;
+    }
+
+    void setSanitizedValue(@Nullable AutofillValue value) {
+        mSanitizedValue = value;
+    }
+
+    @Nullable
     FillResponse getResponse() {
         return mResponse;
     }
@@ -218,6 +228,7 @@
         }
         pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
         pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+        pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
         pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
     }
 }
\ No newline at end of file
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 633bb3e..3d81baf 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -43,7 +43,8 @@
     android.hardware.oemlock-V1.0-java \
     android.hardware.tetheroffload.control-V1.0-java \
     android.hardware.vibrator-V1.0-java \
-    android.hardware.configstore-V1.0-java
+    android.hardware.configstore-V1.0-java \
+    android.hardware.contexthub-V1.0-java
 
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/Tuner.java
index e6ae320..2ea4271 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/Tuner.java
@@ -27,8 +27,10 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 class Tuner extends ITuner.Stub {
     private static final String TAG = "BroadcastRadioService.Tuner";
@@ -96,6 +98,10 @@
     private native boolean nativeIsAnalogForced(long nativeContext);
     private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
 
+    private native Map<String, String> nativeSetParameters(long nativeContext,
+            Map<String, String> parameters);
+    private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
+
     private native boolean nativeIsAntennaConnected(long nativeContext);
 
     @Override
@@ -273,6 +279,31 @@
     }
 
     @Override
+    public Map setParameters(Map parameters) {
+        Map<String, String> results;
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
+        }
+        if (results == null) return Collections.emptyMap();
+        return results;
+    }
+
+    @Override
+    public Map getParameters(List<String> keys) {
+        if (keys == null) {
+            throw new IllegalArgumentException("The argument must not be a null pointer");
+        }
+        Map<String, String> results;
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            results = nativeGetParameters(mNativeContext, keys);
+        }
+        if (results == null) return Collections.emptyMap();
+        return results;
+    }
+
+    @Override
     public boolean isAntennaConnected() {
         synchronized (mLock) {
             checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
index a87ae8d..2460c67 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
@@ -26,6 +26,9 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.util.List;
+import java.util.Map;
+
 class TunerCallback implements ITunerCallback {
     private static final String TAG = "BroadcastRadioService.TunerCallback";
 
@@ -121,6 +124,11 @@
     }
 
     @Override
+    public void onParametersUpdated(Map parameters) {
+        dispatch(() -> mClientCallback.onParametersUpdated(parameters));
+    }
+
+    @Override
     public IBinder asBinder() {
         throw new RuntimeException("Not a binder");
     }
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4bdbbe3..e243e56 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetdEventCallback;
+import android.net.MacAddress;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.metrics.ConnectStats;
@@ -35,6 +36,7 @@
 import android.util.Log;
 import android.util.ArrayMap;
 import android.util.SparseArray;
+import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -242,13 +244,17 @@
         event.timestampMs = timestampMs;
         event.uid = uid;
         event.ethertype = ethertype;
-        event.dstHwAddr = dstHw;
+        event.dstHwAddr = new MacAddress(dstHw);
         event.srcIp = srcIp;
         event.dstIp = dstIp;
         event.ipNextHeader = ipNextHeader;
         event.srcPort = srcPort;
         event.dstPort = dstPort;
         addWakeupEvent(event);
+
+        String dstMac = event.dstHwAddr.toString();
+        StatsLog.write(StatsLog.PACKET_WAKEUP_OCCURRED,
+                uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
     }
 
     private void addWakeupEvent(WakeupEvent event) {
diff --git a/services/core/java/com/android/server/display/BrightnessIdleJob.java b/services/core/java/com/android/server/display/BrightnessIdleJob.java
new file mode 100644
index 0000000..876acf4
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessIdleJob.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.display;
+
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * JobService used to persists brightness slider events when the device
+ * is idle and charging.
+ */
+public class BrightnessIdleJob extends JobService {
+
+    // Must be unique within the system server uid.
+    private static final int JOB_ID = 3923512;
+
+    public static void scheduleJob(Context context) {
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+
+        JobInfo pending = jobScheduler.getPendingJob(JOB_ID);
+        JobInfo jobInfo =
+                new JobInfo.Builder(JOB_ID, new ComponentName(context, BrightnessIdleJob.class))
+                        .setRequiresDeviceIdle(true)
+                        .setRequiresCharging(true)
+                        .setPeriodic(TimeUnit.HOURS.toMillis(24)).build();
+
+        if (pending != null && !pending.equals(jobInfo)) {
+            jobScheduler.cancel(JOB_ID);
+            pending = null;
+        }
+
+        if (pending == null) {
+            jobScheduler.schedule(jobInfo);
+        }
+    }
+
+    public static void cancelJob(Context context) {
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        jobScheduler.cancel(JOB_ID);
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        if (BrightnessTracker.DEBUG) {
+            Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events");
+        }
+        DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class);
+        dmi.persistBrightnessSliderEvents();
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 361d928..90888f0 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -61,12 +61,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 
 import java.util.Deque;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -75,8 +75,8 @@
  */
 public class BrightnessTracker {
 
-    private static final String TAG = "BrightnessTracker";
-    private static final boolean DEBUG = false;
+    static final String TAG = "BrightnessTracker";
+    static final boolean DEBUG = false;
 
     private static final String EVENTS_FILE = "brightness_events.xml";
     private static final int MAX_EVENTS = 100;
@@ -103,6 +103,8 @@
     @GuardedBy("mEventsLock")
     private RingBuffer<BrightnessChangeEvent> mEvents
             = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
+    @GuardedBy("mEventsLock")
+    private boolean mEventsDirty;
     private final Runnable mEventsWriter = () -> writeEvents();
     private volatile boolean mWriteEventsScheduled;
 
@@ -170,6 +172,8 @@
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         mBroadcastReceiver = new Receiver();
         mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
+
+        mInjector.scheduleIdleJob(mContext);
     }
 
     /** Stop listening for events */
@@ -181,6 +185,7 @@
         mInjector.unregisterSensorListener(mContext, mSensorListener);
         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
         mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver);
+        mInjector.cancelIdleJob(mContext);
     }
 
     /**
@@ -211,6 +216,10 @@
                 brightness, userId);
     }
 
+    public void persistEvents() {
+        scheduleWriteEvents();
+    }
+
     private void handleBrightnessChanged() {
         if (DEBUG) {
             Slog.d(TAG, "Brightness change");
@@ -278,6 +287,7 @@
             Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
         }
         synchronized (mEventsLock) {
+            mEventsDirty = true;
             mEvents.append(event);
         }
     }
@@ -291,8 +301,12 @@
 
     private void writeEvents() {
         mWriteEventsScheduled = false;
-        // TODO kick off write on handler thread e.g. every 24 hours.
         synchronized (mEventsLock) {
+            if (!mEventsDirty) {
+                // Nothing to write
+                return;
+            }
+
             final AtomicFile writeTo = mInjector.getFile();
             if (writeTo == null) {
                 return;
@@ -301,12 +315,14 @@
                 if (writeTo.exists()) {
                     writeTo.delete();
                 }
+                mEventsDirty = false;
             } else {
                 FileOutputStream output = null;
                 try {
                     output = writeTo.startWrite();
                     writeEventsLocked(output);
                     writeTo.finishWrite(output);
+                    mEventsDirty = false;
                 } catch (IOException e) {
                     writeTo.failWrite(output);
                     Slog.e(TAG, "Failed to write change mEvents.", e);
@@ -317,6 +333,8 @@
 
     private void readEvents() {
         synchronized (mEventsLock) {
+            // Read might prune events so mark as dirty.
+            mEventsDirty = true;
             mEvents.clear();
             final AtomicFile readFrom = mInjector.getFile();
             if (readFrom != null && readFrom.exists()) {
@@ -344,13 +362,16 @@
 
         out.startTag(null, TAG_EVENTS);
         BrightnessChangeEvent[] toWrite = mEvents.toArray();
+        // Clear events, code below will add back the ones that are still within the time window.
+        mEvents.clear();
         if (DEBUG) {
             Slog.d(TAG, "Writing events " + toWrite.length);
         }
-        final long timeCutOff = System.currentTimeMillis() - MAX_EVENT_AGE;
+        final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
         for (int i = 0; i < toWrite.length; ++i) {
             int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
+                mEvents.append(toWrite[i]);
                 out.startTag(null, TAG_EVENT);
                 out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness));
                 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
@@ -465,6 +486,17 @@
         }
     }
 
+    public void dump(PrintWriter pw) {
+        synchronized (mEventsLock) {
+            pw.println("BrightnessTracker state:");
+            pw.println("  mEvents.size=" + mEvents.size());
+            pw.println("  mEventsDirty=" + mEventsDirty);
+        }
+        synchronized (mDataCollectionLock) {
+            pw.println("  mLastSensorReadings.size=" + mLastSensorReadings.size());
+        }
+    }
+
     // Not allowed to keep the SensorEvent so used to copy the data we care about.
     private static class LightData {
         public float lux;
@@ -635,5 +667,13 @@
         public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
             return ActivityManager.getService().getFocusedStackInfo();
         }
+
+        public void scheduleIdleJob(Context context) {
+            BrightnessIdleJob.scheduleJob(context);
+        }
+
+        public void cancelIdleJob(Context context) {
+            BrightnessIdleJob.cancelJob(context);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index f1e2011..7530f3e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1285,6 +1285,9 @@
 
             pw.println();
             mPersistentDataStore.dump(pw);
+
+            pw.println();
+            mBrightnessTracker.dump(pw);
         }
     }
 
@@ -1921,5 +1924,10 @@
         public boolean isUidPresentOnDisplay(int uid, int displayId) {
             return isUidPresentOnDisplayInternal(uid, displayId);
         }
+
+        @Override
+        public void persistBrightnessSliderEvents() {
+            mBrightnessTracker.persistEvents();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 5e9f355..da481a8 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -18,15 +18,25 @@
 
 import android.Manifest;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.contexthub.V1_0.AsyncEventType;
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.IContexthubCallback;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
 import android.hardware.location.ContextHubInfo;
-import android.hardware.location.ContextHubManager;
 import android.hardware.location.ContextHubMessage;
-import android.hardware.location.IContextHubService;
 import android.hardware.location.IContextHubCallback;
-import android.hardware.location.NanoAppFilter;
+import android.hardware.location.IContextHubService;
+import android.hardware.location.IContextHubTransactionCallback;
 import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppFilter;
 import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
@@ -38,8 +48,10 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.HashMap;
 
 /**
  * @hide
@@ -48,55 +60,152 @@
     private static final String TAG = "ContextHubService";
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
-        + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
-    public static final int ANY_HUB             = -1;
-    public static final int MSG_LOAD_NANO_APP   = 3;
+    /*
+     * Constants for the type of transaction that is defined by ContextHubService.
+     * This is used to report the transaction callback to clients, and is different from
+     * ContextHubTransaction.Type.
+     */
+    public static final int MSG_ENABLE_NANO_APP = 1;
+    public static final int MSG_DISABLE_NANO_APP = 2;
+    public static final int MSG_LOAD_NANO_APP = 3;
     public static final int MSG_UNLOAD_NANO_APP = 4;
+    public static final int MSG_QUERY_NANO_APPS = 5;
+    public static final int MSG_QUERY_MEMORY = 6;
+    public static final int MSG_HUB_RESET = 7;
 
     private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
     private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
     private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
     private static final int PRE_LOADED_APP_MEM_REQ = 0;
 
-    private static final int MSG_HEADER_SIZE = 4;
-    private static final int HEADER_FIELD_MSG_TYPE = 0;
-    private static final int HEADER_FIELD_MSG_VERSION = 1;
-    private static final int HEADER_FIELD_HUB_HANDLE = 2;
-    private static final int HEADER_FIELD_APP_INSTANCE = 3;
-
-    private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-    private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-    private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2;
-
     private static final int OS_APP_INSTANCE = -1;
 
     private final Context mContext;
+
+    // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
+    // Service cache maintaining of instance ID to nanoapp infos
     private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
             new ConcurrentHashMap<>();
+    // The next available instance ID (managed by the service) to assign to a nanoapp
+    private int mNextAvailableInstanceId = 0;
+    // A map of the long nanoapp ID to instance ID managed by the service
+    private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
+            new ConcurrentHashMap<>();
+
     private final ContextHubInfo[] mContextHubInfo;
     private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
             new RemoteCallbackList<>();
 
-    private native int nativeSendMessage(int[] header, byte[] data);
-    private native ContextHubInfo[] nativeInitialize();
+    // Proxy object to communicate with the Context Hub HAL
+    private final IContexthub mContextHubProxy;
+
+    // The manager for transaction queue
+    private final ContextHubTransactionManager mTransactionManager;
+
+    /**
+     * Class extending the callback to register with a Context Hub.
+     */
+    private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+        private final int mContextHubId;
+
+        ContextHubServiceCallback(int contextHubId) {
+            mContextHubId = contextHubId;
+        }
+
+        @Override
+        public void handleClientMsg(ContextHubMsg message) {
+            handleClientMessageCallback(mContextHubId, message);
+        }
+
+        @Override
+        public void handleTxnResult(int transactionId, int result) {
+            handleTransactionResultCallback(mContextHubId, transactionId, result);
+        }
+
+        @Override
+        public void handleHubEvent(int eventType) {
+            handleHubEventCallback(mContextHubId, eventType);
+        }
+
+        @Override
+        public void handleAppAbort(long nanoAppId, int abortCode) {
+            handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+        }
+
+        @Override
+        public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
+            handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
+        }
+    }
 
     public ContextHubService(Context context) {
         mContext = context;
-        mContextHubInfo = nativeInitialize();
+
+        mContextHubProxy = getContextHubProxy();
+        if (mContextHubProxy == null) {
+            mTransactionManager = null;
+            mContextHubInfo = new ContextHubInfo[0];
+            return;
+        }
+
+        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+
+        List<ContextHub> hubList;
+        try {
+            hubList = mContextHubProxy.getHubs();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while getting Context Hub info");
+            hubList = Collections.emptyList();
+        }
+        mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            int contextHubId = contextHubInfo.getId();
+            try {
+                mContextHubProxy.registerCallback(
+                        contextHubId, new ContextHubServiceCallback(contextHubId));
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
+                        + contextHubId + ")");
+            }
+        }
+
+        // Do a query to initialize the service cache list of nanoapps
+        // TODO(b/69270990): Remove this when old API is deprecated
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            queryNanoAppsInternal(contextHubInfo.getId());
+        }
 
         for (int i = 0; i < mContextHubInfo.length; i++) {
             Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
-                  + ", name:  " + mContextHubInfo[i].getName());
+                    + ", name:  " + mContextHubInfo[i].getName());
         }
     }
 
+    /**
+     * @return the IContexthub proxy interface
+     */
+    private IContexthub getContextHubProxy() {
+        IContexthub proxy = null;
+        try {
+            proxy = IContexthub.getService(true /* retry */);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+        } catch (NoSuchElementException e) {
+            Log.i(TAG, "Context Hub HAL service not found");
+        }
+
+        return proxy;
+    }
+
     @Override
     public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
         mCallbacksList.register(callback);
         Log.d(TAG, "Added callback, total callbacks " +
-              mCallbacksList.getRegisteredCallbackCount());
+                mCallbacksList.getRegisteredCallbackCount());
         return 0;
     }
 
@@ -109,29 +218,112 @@
         for (int i = 0; i < returnArray.length; ++i) {
             returnArray[i] = i;
             Log.d(TAG, String.format("Hub %s is mapped to %d",
-                                     mContextHubInfo[i].getName(), returnArray[i]));
+                    mContextHubInfo[i].getName(), returnArray[i]));
         }
 
         return returnArray;
     }
 
     @Override
-    public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+    public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException {
         checkPermissions();
-        if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
-            Log.e(TAG, "Invalid context hub handle " + contextHubHandle);
+        if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+            Log.e(TAG, "Invalid context hub handle " + contextHubId);
             return null; // null means fail
         }
 
-        return mContextHubInfo[contextHubHandle];
+        return mContextHubInfo[contextHubId];
+    }
+
+    /**
+     * Creates an internal load transaction callback to be used for old API clients
+     *
+     * @param contextHubId  the ID of the hub to load the binary
+     * @param nanoAppBinary the binary to load
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createLoadTransactionCallback(
+            int contextHubId, NanoAppBinary nanoAppBinary) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+                handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+            }
+        };
+    }
+
+    /**
+     * Creates an internal unload transaction callback to be used for old API clients
+     *
+     * @param contextHubId the ID of the hub to unload the nanoapp
+     * @param nanoAppId    the ID of the nanoapp to unload
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createUnloadTransactionCallback(
+            int contextHubId, long nanoAppId) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+                handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+            }
+        };
+    }
+
+    /**
+     * Creates an internal query transaction callback to be used for old API clients
+     *
+     * @param contextHubId the ID of the hub to query
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+                byte[] data = {(byte) result};
+                onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+            }
+        };
+    }
+
+    /**
+     * Adds a new transaction to the transaction manager queue
+     *
+     * @param transaction the transaction to add
+     * @return the result of adding the transaction
+     */
+    private int addTransaction(ContextHubServiceTransaction transaction) {
+        int result = Result.OK;
+        try {
+            mTransactionManager.addTransaction(transaction);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, e.getMessage());
+            result = Result.TRANSACTION_PENDING; /* failed */
+        }
+
+        return result;
     }
 
     @Override
-    public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
+    public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException {
         checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
 
-        if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
-            Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+        if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+            Log.e(TAG, "Invalid contextHubhandle " + contextHubId);
             return -1;
         }
         if (app == null) {
@@ -139,20 +331,17 @@
             return -1;
         }
 
-        int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
-        msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
+        // Create an internal IContextHubTransactionCallback for the old API clients
+        NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary());
+        IContextHubTransactionCallback onCompleteCallback =
+                createLoadTransactionCallback(contextHubId, nanoAppBinary);
 
-        long appId = app.getAppId();
+        ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
+                contextHubId, nanoAppBinary, onCompleteCallback);
 
-        msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
-        msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
-
-        int errVal = nativeSendMessage(msgHeader, app.getAppBinary());
-        if (errVal != 0) {
-            Log.e(TAG, "Send Message returns error" + contextHubHandle);
+        int result = addTransaction(transaction);
+        if (result != Result.OK) {
+            Log.e(TAG, "Failed to load nanoapp with error code " + result);
             return -1;
         }
 
@@ -163,23 +352,26 @@
     @Override
     public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
         checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
+
         NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
         if (info == null) {
             Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
             return -1; //means failed
         }
 
-        // Call Native interface here
-        int[] msgHeader = new int[MSG_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
-        msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP;
+        int contextHubId = info.getContexthubId();
+        long nanoAppId = info.getAppId();
+        IContextHubTransactionCallback onCompleteCallback =
+                createUnloadTransactionCallback(contextHubId, nanoAppId);
+        ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
+                contextHubId, nanoAppId, onCompleteCallback);
 
-        byte msg[] = new byte[0];
-
-        if (nativeSendMessage(msgHeader, msg) != 0) {
-            Log.e(TAG, "native send message fails");
+        int result = addTransaction(transaction);
+        if (result != Result.OK) {
+            Log.e(TAG, "Failed to unload nanoapp with error code " + result);
             return -1;
         }
 
@@ -189,7 +381,7 @@
 
     @Override
     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
-                                                      throws RemoteException {
+            throws RemoteException {
         checkPermissions();
         // This assumes that all the nanoAppInfo is current. This is reasonable
         // for the use cases for tightly controlled nanoApps.
@@ -206,7 +398,7 @@
         checkPermissions();
         ArrayList<Integer> foundInstances = new ArrayList<Integer>();
 
-        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
             NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
 
             if (filter.testMatch(info)) {
@@ -223,23 +415,230 @@
         return retArray;
     }
 
-    @Override
-    public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
-                           throws RemoteException {
-        checkPermissions();
+    /**
+     * Performs a query at the specified hub.
+     *
+     * This method should only be invoked internally by the service, either to update the service
+     * cache or as a result of an explicit query requested by a client through the sendMessage API.
+     *
+     * @param contextHubId the ID of the hub to do the query
+     * @return the result of the query
+     */
+    private int queryNanoAppsInternal(int contextHubId) {
+        if (mContextHubProxy == null) {
+            return Result.UNKNOWN_FAILURE;
+        }
 
-        if (msg == null || msg.getData() == null) {
-            Log.w(TAG, "null ptr");
+        IContextHubTransactionCallback onCompleteCallback =
+                createQueryTransactionCallback(contextHubId);
+        ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+                contextHubId, onCompleteCallback);
+
+        return addTransaction(transaction);
+    }
+
+    @Override
+    public int sendMessage(
+            int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
+        checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
+        if (msg == null) {
+            Log.e(TAG, "ContextHubMessage cannot be null");
+            return -1;
+        }
+        if (msg.getData() == null) {
+            Log.w(TAG, "ContextHubMessage message body cannot be null");
             return -1;
         }
 
-        int[] msgHeader = new int[MSG_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion();
-        msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType();
+        int result;
+        if (nanoAppHandle == OS_APP_INSTANCE) {
+            if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
+                result = queryNanoAppsInternal(hubHandle);
+            } else {
+                Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
+                result = Result.BAD_PARAMS;
+            }
+        } else {
+            NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
+            if (info != null) {
+                ContextHubMsg hubMessage = new ContextHubMsg();
+                hubMessage.appName = info.getAppId();
+                hubMessage.msgType = msg.getMsgType();
+                hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
+                ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
 
-        return nativeSendMessage(msgHeader, msg.getData());
+                try {
+                    result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to send nanoapp message - RemoteException");
+                    result = Result.UNKNOWN_FAILURE;
+                }
+            } else {
+                Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+                        + nanoAppHandle + " does not exist.");
+                result = Result.BAD_PARAMS;
+            }
+        }
+
+        return (result == Result.OK ? 0 : -1);
+    }
+
+    /**
+     * Handles a unicast or broadcast message from a nanoapp.
+     *
+     * @param contextHubId the ID of the hub the message came from
+     * @param message      the message contents
+     */
+    private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
+        // TODO(b/67734082): Send to new API clients
+        byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
+
+        int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
+                mNanoAppIdToInstanceMap.get(message.appName) : -1;
+        onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+    }
+
+    /**
+     * A helper function to handle a load response from the Context Hub for the old API.
+     *
+     * TODO(b/69270990): Remove this once the old APIs are obsolete.
+     */
+    private void handleLoadResponseOldApi(
+            int contextHubId, int result, NanoAppBinary nanoAppBinary) {
+        if (nanoAppBinary == null) {
+            Log.e(TAG, "Nanoapp binary field was null for a load transaction");
+            return;
+        }
+
+        // NOTE: The legacy JNI code used to do a query right after a load success
+        // to synchronize the service cache. Instead store the binary that was requested to
+        // load to update the cache later without doing a query.
+        int instanceId = 0;
+        long nanoAppId = nanoAppBinary.getNanoAppId();
+        int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
+        if (result == TransactionResult.SUCCESS) {
+            if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+                instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+            } else {
+                instanceId = mNextAvailableInstanceId++;
+                mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+            }
+
+            addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
+        }
+
+        byte[] data = new byte[5];
+        data[0] = (byte) result;
+        ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
+
+        onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+    }
+
+    /**
+     * A helper function to handle an unload response from the Context Hub for the old API.
+     *
+     * TODO(b/69270990): Remove this once the old APIs are obsolete.
+     */
+    private void handleUnloadResponseOldApi(
+            int contextHubId, int result, long nanoAppId) {
+        if (result == TransactionResult.SUCCESS) {
+            int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+            deleteAppInstance(instanceId);
+            mNanoAppIdToInstanceMap.remove(nanoAppId);
+        }
+
+        byte[] data = new byte[1];
+        data[0] = (byte) result;
+        onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+    }
+
+    /**
+     * Handles a transaction response from a Context Hub.
+     *
+     * @param contextHubId  the ID of the hub the response came from
+     * @param transactionId the ID of the transaction
+     * @param result        the result of the transaction reported by the hub
+     */
+    private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
+        mTransactionManager.onTransactionResponse(transactionId, result);
+    }
+
+    /**
+     * Handles an asynchronous event from a Context Hub.
+     *
+     * @param contextHubId the ID of the hub the response came from
+     * @param eventType    the type of the event as defined in Context Hub HAL AsyncEventType
+     */
+    private void handleHubEventCallback(int contextHubId, int eventType) {
+        if (eventType == AsyncEventType.RESTARTED) {
+            mTransactionManager.onHubReset();
+            queryNanoAppsInternal(contextHubId);
+
+            byte[] data = {TransactionResult.SUCCESS};
+            onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+        } else {
+            Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
+                    + eventType + ")");
+        }
+    }
+
+    /**
+     * Handles an asynchronous abort event of a nanoapp.
+     *
+     * @param contextHubId the ID of the hub that the nanoapp aborted in
+     * @param nanoAppId    the ID of the aborted nanoapp
+     * @param abortCode    the nanoapp-specific abort code
+     */
+    private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
+        // TODO(b/31049861): Implement this
+    }
+
+    /**
+     * Handles a query response from a Context Hub.
+     *
+     * @param contextHubId    the ID of the hub of the response
+     * @param nanoAppInfoList the list of loaded nanoapps
+     */
+    private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+        List<NanoAppState> nanoAppStateList =
+                ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+
+        updateServiceCache(contextHubId, nanoAppInfoList);
+        mTransactionManager.onQueryResponse(nanoAppStateList);
+    }
+
+    /**
+     * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
+     *
+     * TODO(b/69270990): Remove this when the old API functionality is removed.
+     *
+     * @param contextHubId    the ID of the hub the response came from
+     * @param nanoAppInfoList the list of loaded nanoapps
+     */
+    private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+        synchronized (mNanoAppHash) {
+            for (int instanceId : mNanoAppHash.keySet()) {
+                if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
+                    deleteAppInstance(instanceId);
+                }
+            }
+
+            for (HubAppInfo appInfo : nanoAppInfoList) {
+                int instanceId;
+                long nanoAppId = appInfo.appId;
+                if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+                    instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+                } else {
+                    instanceId = mNextAvailableInstanceId++;
+                    mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+                }
+
+                addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
+            }
+        }
     }
 
     @Override
@@ -257,7 +656,7 @@
         pw.println("");
         pw.println("=================== NANOAPPS ====================");
         // Dump nanoAppHash
-        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
             pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
         }
 
@@ -268,19 +667,15 @@
         mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
 
-    private int onMessageReceipt(int[] header, byte[] data) {
-        if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
-            return  -1;
+    private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+        if (data == null) {
+            return -1;
         }
 
+        int msgVersion = 0;
         int callbacksCount = mCallbacksList.beginBroadcast();
-        int msgType = header[HEADER_FIELD_MSG_TYPE];
-        int msgVersion = header[HEADER_FIELD_MSG_VERSION];
-        int hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-        int appInstance = header[HEADER_FIELD_APP_INSTANCE];
-
         Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
-              hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
+                hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
 
         if (callbacksCount < 1) {
             Log.v(TAG, "No message callbacks registered.");
@@ -323,8 +718,8 @@
         }
 
         mNanoAppHash.put(appInstanceHandle, appInfo);
-        Log.d(TAG, action + " app instance " + appInstanceHandle + " with id "
-              + appId + " version " + appVersion);
+        Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
+                + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));
 
         return 0;
     }
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
new file mode 100644
index 0000000..66145bb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 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.location;
+
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppState;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract class representing transactions requested to the Context Hub Service.
+ *
+ * @hide
+ */
+/* package */ abstract class ContextHubServiceTransaction {
+    private final int mTransactionId;
+    @ContextHubTransaction.Type
+    private final int mTransactionType;
+
+    /*
+     * true if the transaction has already completed, false otherwise
+     */
+    private boolean mIsComplete = false;
+
+    /* package */ ContextHubServiceTransaction(int id, int type) {
+        mTransactionId = id;
+        mTransactionType = type;
+    }
+
+    /**
+     * Starts this transaction with a Context Hub.
+     *
+     * All instances of this class must implement this method by making an asynchronous request to
+     * a hub.
+     *
+     * @return the synchronous error code of the transaction start
+     */
+    /* package */
+    abstract int onTransact();
+
+    /**
+     * A function to invoke when a transaction times out.
+     *
+     * All instances of this class must implement this method by reporting the timeout to the
+     * client.
+     */
+    /* package */
+    abstract void onTimeout();
+
+    /**
+     * A function to invoke when the transaction completes.
+     *
+     * Only relevant for load, unload, enable, or disable transactions.
+     *
+     * @param result the result of the transaction
+     */
+    /* package */ void onTransactionComplete(int result) {
+    }
+
+    /**
+     * A function to invoke when a query transaction completes.
+     *
+     * Only relevant for query transactions.
+     *
+     * @param result           the result of the query
+     * @param nanoAppStateList the list of nanoapps given by the query response
+     */
+    /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+    }
+
+    /**
+     * @return the ID of this transaction
+     */
+    /* package */ int getTransactionId() {
+        return mTransactionId;
+    }
+
+    /**
+     * @return the type of this transaction
+     * @see ContextHubTransaction.Type
+     */
+    @ContextHubTransaction.Type
+    /* package */ int getTransactionType() {
+        return mTransactionType;
+    }
+
+    /**
+     * Gets the timeout period as defined in IContexthub.hal
+     *
+     * @return the timeout of this transaction in the specified time unit
+     */
+    /* package */ long getTimeout(TimeUnit unit) {
+        switch (mTransactionType) {
+            case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+                return unit.convert(30L, TimeUnit.SECONDS);
+            case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+            case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+            case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+            case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+                // Note: query timeout is not specified at the HAL
+            default: /* fall through */
+                return unit.convert(5L, TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * Marks the transaction as complete.
+     *
+     * Should only be called as a result of a response from a Context Hub callback
+     */
+    /* package */ void setComplete() {
+        mIsComplete = true;
+    }
+
+    /**
+     * @return true if the transaction has already completed, false otherwise
+     */
+    /* package */ boolean isComplete() {
+        return mIsComplete;
+    }
+
+    /**
+     * @return the human-readable string of this transaction's type
+     */
+    private String getTransactionTypeString() {
+        switch (mTransactionType) {
+            case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+                return "Load";
+            case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+                return "Unload";
+            case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+                return "Enable";
+            case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+                return "Disable";
+            case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+                return "Query";
+            default:
+                return "Unknown";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getTransactionTypeString() + " transaction (ID = " + mTransactionId + ")";
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
new file mode 100644
index 0000000..ddbaf86
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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.location;
+
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A class encapsulating helper functions used by the ContextHubService class
+ */
+/* package */ class ContextHubServiceUtil {
+    private static final String TAG = "ContextHubServiceUtil";
+
+    /**
+     * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
+     *
+     * @param hubList the ContextHub ArrayList
+     * @return the ContextHubInfo array
+     */
+    /* package */
+    static ContextHubInfo[] createContextHubInfoArray(List<ContextHub> hubList) {
+        ContextHubInfo[] contextHubInfoList = new ContextHubInfo[hubList.size()];
+        for (int i = 0; i < hubList.size(); i++) {
+            contextHubInfoList[i] = new ContextHubInfo(hubList.get(i));
+        }
+
+        return contextHubInfoList;
+    }
+
+    /**
+     * Copies a primitive byte array to a ArrayList<Byte>.
+     *
+     * @param inputArray  the primitive byte array
+     * @param outputArray the ArrayList<Byte> array to append
+     */
+    /* package */
+    static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) {
+        outputArray.clear();
+        outputArray.ensureCapacity(inputArray.length);
+        for (byte element : inputArray) {
+            outputArray.add(element);
+        }
+    }
+
+    /**
+     * Creates a byte array given a ArrayList<Byte> and copies its contents.
+     *
+     * @param array the ArrayList<Byte> object
+     * @return the byte array
+     */
+    /* package */
+    static byte[] createPrimitiveByteArray(ArrayList<Byte> array) {
+        byte[] primitiveArray = new byte[array.size()];
+        for (int i = 0; i < array.size(); i++) {
+            primitiveArray[i] = array.get(i);
+        }
+
+        return primitiveArray;
+    }
+
+    /**
+     * Generates the Context Hub HAL's NanoAppBinary object from the client-facing
+     * android.hardware.location.NanoAppBinary object.
+     *
+     * @param nanoAppBinary the client-facing NanoAppBinary object
+     * @return the Context Hub HAL's NanoAppBinary object
+     */
+    /* package */
+    static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary(
+            NanoAppBinary nanoAppBinary) {
+        android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+                new android.hardware.contexthub.V1_0.NanoAppBinary();
+
+        hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId();
+        hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion();
+        hidlNanoAppBinary.flags = nanoAppBinary.getFlags();
+        hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
+        hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
+
+        // Log exceptions while processing the binary, but continue to pass down the binary
+        // since the error checking is deferred to the Context Hub.
+        try {
+            copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary);
+        } catch (IndexOutOfBoundsException e) {
+            Log.w(TAG, e.getMessage());
+        } catch (NullPointerException e) {
+            Log.w(TAG, "NanoApp binary was null");
+        }
+
+        return hidlNanoAppBinary;
+    }
+
+    /**
+     * Generates a client-facing NanoAppState array from a HAL HubAppInfo array.
+     *
+     * @param nanoAppInfoList the array of HubAppInfo objects
+     * @return the corresponding array of NanoAppState objects
+     */
+    /* package */
+    static List<NanoAppState> createNanoAppStateList(
+            List<HubAppInfo> nanoAppInfoList) {
+        ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
+        for (HubAppInfo appInfo : nanoAppInfoList) {
+            nanoAppStateList.add(
+                    new NanoAppState(appInfo.appId, appInfo.version, appInfo.enabled));
+        }
+
+        return nanoAppStateList;
+    }
+
+    /**
+     * Creates a HIDL ContextHubMsg object to send to a nanoapp.
+     *
+     * @param hostEndPoint the ID of the client sending the message
+     * @param message      the client-facing NanoAppMessage object describing the message
+     * @return the HIDL ContextHubMsg object
+     */
+    /* package */
+    static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) {
+        ContextHubMsg hidlMessage = new ContextHubMsg();
+
+        hidlMessage.appName = message.getNanoAppId();
+        hidlMessage.hostEndPoint = hostEndPoint;
+        hidlMessage.msgType = message.getMessageType();
+        copyToByteArrayList(message.getMessageBody(), hidlMessage.msg);
+
+        return hidlMessage;
+    }
+
+    /**
+     * Creates a client-facing NanoAppMessage object to send to a client.
+     *
+     * @param message the HIDL ContextHubMsg object from a nanoapp
+     * @return the NanoAppMessage object
+     */
+    /* package */
+    static NanoAppMessage createNanoAppMessage(ContextHubMsg message) {
+        byte[] messageArray = createPrimitiveByteArray(message.msg);
+
+        return NanoAppMessage.createMessageFromNanoApp(
+                message.appName, message.msgType, messageArray,
+                message.hostEndPoint == HostEndPoint.BROADCAST);
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
new file mode 100644
index 0000000..898b76c
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 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.location;
+
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManager {
+    private static final String TAG = "ContextHubTransactionManager";
+
+    /*
+     * Maximum number of transaction requests that can be pending at a time
+     */
+    private static final int MAX_PENDING_REQUESTS = 10;
+
+    /*
+     * The proxy to talk to the Context Hub
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * A queue containing the current transactions
+     */
+    private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+
+    /*
+     * The next available transaction ID
+     */
+    private final AtomicInteger mNextAvailableId = new AtomicInteger();
+
+    /*
+     * An executor and the future object for scheduling timeout timers
+     */
+    private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
+    private ScheduledFuture<?> mTimeoutFuture = null;
+
+    /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+        mContextHubProxy = contextHubProxy;
+    }
+
+    /**
+     * Creates a transaction for loading a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to load the nanoapp to
+     * @param nanoAppBinary      the binary of the nanoapp to load
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createLoadTransaction(
+            int contextHubId, NanoAppBinary nanoAppBinary,
+            IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+                        ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
+                try {
+                    return mContextHubProxy.loadNanoApp(
+                            contextHubId, hidlNanoAppBinary, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppBinary.getNanoAppId()));
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a transaction for unloading a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to load the nanoapp to
+     * @param nanoAppId          the ID of the nanoapp to unload
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createUnloadTransaction(
+            int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.unloadNanoApp(
+                            contextHubId, nanoAppId, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppId));
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a transaction for querying for a list of nanoapps.
+     *
+     * @param contextHubId       the ID of the hub to query
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createQueryTransaction(
+            int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.queryApps(contextHubId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to query for nanoapps");
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onQueryResponse(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT,
+                        Collections.emptyList());
+            }
+
+            @Override
+            /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+                try {
+                    onCompleteCallback.onQueryResponse(result, nanoAppStateList);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onQueryComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Adds a new transaction to the queue.
+     *
+     * If there was no pending transaction at the time, the transaction that was added will be
+     * started in this method.
+     *
+     * @param transaction the transaction to add
+     * @throws IllegalStateException if the queue is full
+     */
+    /* package */
+    synchronized void addTransaction(
+            ContextHubServiceTransaction transaction) throws IllegalStateException {
+        if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
+            throw new IllegalStateException("Transaction transaction queue is full (capacity = "
+                    + MAX_PENDING_REQUESTS + ")");
+        }
+        mTransactionQueue.add(transaction);
+
+        if (mTransactionQueue.size() == 1) {
+            startNextTransaction();
+        }
+    }
+
+    /**
+     * Handles a transaction response from a Context Hub.
+     *
+     * @param transactionId the transaction ID of the response
+     * @param result        the result of the transaction
+     */
+    /* package */
+    synchronized void onTransactionResponse(int transactionId, int result) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionId() != transactionId) {
+            Log.w(TAG, "Received unexpected transaction response (expected ID = "
+                    + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+            return;
+        }
+
+        transaction.onTransactionComplete(result);
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Handles a query response from a Context Hub.
+     *
+     * @param nanoAppStateList the list of nanoapps included in the response
+     */
+    /* package */
+    synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected query response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+            Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+            return;
+        }
+
+        transaction.onQueryResponse(TransactionResult.SUCCESS, nanoAppStateList);
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Handles a hub reset event by stopping a pending transaction and starting the next.
+     */
+    /* package */
+    synchronized void onHubReset() {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            return;
+        }
+
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Pops the front transaction from the queue and starts the next pending transaction request.
+     *
+     * Removing elements from the transaction queue must only be done through this method. When a
+     * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+     * complete.
+     *
+     * It is assumed that the transaction queue is non-empty when this method is invoked, and that
+     * the caller has obtained a lock on this ContextHubTransactionManager object.
+     */
+    private void removeTransactionAndStartNext() {
+        mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+
+        ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+        transaction.setComplete();
+
+        if (!mTransactionQueue.isEmpty()) {
+            startNextTransaction();
+        }
+    }
+
+    /**
+     * Starts the next pending transaction request.
+     *
+     * Starting new transactions must only be done through this method. This method continues to
+     * process the transaction queue as long as there are pending requests, and no transaction is
+     * pending.
+     *
+     * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+     * object.
+     */
+    private void startNextTransaction() {
+        int result = Result.UNKNOWN_FAILURE;
+        while (result != Result.OK && !mTransactionQueue.isEmpty()) {
+            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            result = transaction.onTransact();
+
+            if (result == Result.OK) {
+                Runnable onTimeoutFunc = () -> {
+                    synchronized (this) {
+                        if (!transaction.isComplete()) {
+                            Log.d(TAG, transaction + " timed out");
+                            transaction.onTimeout();
+
+                            removeTransactionAndStartNext();
+                        }
+                    }
+                };
+
+                long timeoutSeconds = transaction.getTimeout(TimeUnit.SECONDS);
+                mTimeoutFuture = mTimeoutExecutor.schedule(onTimeoutFunc, timeoutSeconds,
+                        TimeUnit.SECONDS);
+            } else {
+                mTransactionQueue.remove();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a1a0106..94a4dc8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -603,160 +603,156 @@
     }
 
     private void migrateOldData() {
-        try {
-            // These Settings moved before multi-user was enabled, so we only have to do it for the
-            // root user.
-            if (getString("migrated", null, 0) == null) {
-                final ContentResolver cr = mContext.getContentResolver();
-                for (String validSetting : VALID_SETTINGS) {
-                    String value = Settings.Secure.getString(cr, validSetting);
-                    if (value != null) {
-                        setString(validSetting, value, 0);
-                    }
+        // These Settings moved before multi-user was enabled, so we only have to do it for the
+        // root user.
+        if (getString("migrated", null, 0) == null) {
+            final ContentResolver cr = mContext.getContentResolver();
+            for (String validSetting : VALID_SETTINGS) {
+                String value = Settings.Secure.getString(cr, validSetting);
+                if (value != null) {
+                    setString(validSetting, value, 0);
                 }
-                // No need to move the password / pattern files. They're already in the right place.
-                setString("migrated", "true", 0);
-                Slog.i(TAG, "Migrated lock settings to new location");
             }
+            // No need to move the password / pattern files. They're already in the right place.
+            setString("migrated", "true", 0);
+            Slog.i(TAG, "Migrated lock settings to new location");
+        }
 
-            // These Settings changed after multi-user was enabled, hence need to be moved per user.
-            if (getString("migrated_user_specific", null, 0) == null) {
-                final ContentResolver cr = mContext.getContentResolver();
-                List<UserInfo> users = mUserManager.getUsers();
-                for (int user = 0; user < users.size(); user++) {
-                    // Migrate owner info
-                    final int userId = users.get(user).id;
-                    final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
-                    String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
-                    if (!TextUtils.isEmpty(ownerInfo)) {
-                        setString(OWNER_INFO, ownerInfo, userId);
-                        Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
-                    }
-
-                    // Migrate owner info enabled. Note there was a bug where older platforms only
-                    // stored this value if the checkbox was toggled at least once. The code detects
-                    // this case by handling the exception.
-                    final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
-                    boolean enabled;
-                    try {
-                        int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
-                        enabled = ivalue != 0;
-                        setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
-                    } catch (SettingNotFoundException e) {
-                        // Setting was never stored. Store it if the string is not empty.
-                        if (!TextUtils.isEmpty(ownerInfo)) {
-                            setLong(OWNER_INFO_ENABLED, 1, userId);
-                        }
-                    }
-                    Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
-                }
-                // No need to move the password / pattern files. They're already in the right place.
-                setString("migrated_user_specific", "true", 0);
-                Slog.i(TAG, "Migrated per-user lock settings to new location");
-            }
-
-            // Migrates biometric weak such that the fallback mechanism becomes the primary.
-            if (getString("migrated_biometric_weak", null, 0) == null) {
-                List<UserInfo> users = mUserManager.getUsers();
-                for (int i = 0; i < users.size(); i++) {
-                    int userId = users.get(i).id;
-                    long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
-                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                            userId);
-                    long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
-                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                            userId);
-                    if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
-                        setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
-                                alternateType,
-                                userId);
-                    }
-                    setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
-                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                            userId);
-                }
-                setString("migrated_biometric_weak", "true", 0);
-                Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
-            }
-
-            // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
-            // user was present on the system, so if we're upgrading to M and there is more than one
-            // user we disable the flag to remain consistent.
-            if (getString("migrated_lockscreen_disabled", null, 0) == null) {
-                final List<UserInfo> users = mUserManager.getUsers();
-                final int userCount = users.size();
-                int switchableUsers = 0;
-                for (int i = 0; i < userCount; i++) {
-                    if (users.get(i).supportsSwitchTo()) {
-                        switchableUsers++;
-                    }
+        // These Settings changed after multi-user was enabled, hence need to be moved per user.
+        if (getString("migrated_user_specific", null, 0) == null) {
+            final ContentResolver cr = mContext.getContentResolver();
+            List<UserInfo> users = mUserManager.getUsers();
+            for (int user = 0; user < users.size(); user++) {
+                // Migrate owner info
+                final int userId = users.get(user).id;
+                final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
+                String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
+                if (!TextUtils.isEmpty(ownerInfo)) {
+                    setString(OWNER_INFO, ownerInfo, userId);
+                    Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
                 }
 
-                if (switchableUsers > 1) {
-                    for (int i = 0; i < userCount; i++) {
-                        int id = users.get(i).id;
-
-                        if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
-                            setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
-                        }
-                    }
-                }
-
-                setString("migrated_lockscreen_disabled", "true", 0);
-                Slog.i(TAG, "Migrated lockscreen disabled flag");
-            }
-
-            final List<UserInfo> users = mUserManager.getUsers();
-            for (int i = 0; i < users.size(); i++) {
-                final UserInfo userInfo = users.get(i);
-                if (userInfo.isManagedProfile() && mStorage.hasChildProfileLock(userInfo.id)) {
-                    // When managed profile has a unified lock, the password quality stored has 2
-                    // possibilities only.
-                    // 1). PASSWORD_QUALITY_UNSPECIFIED, which is upgraded from dp2, and we are
-                    // going to set it back to PASSWORD_QUALITY_ALPHANUMERIC.
-                    // 2). PASSWORD_QUALITY_ALPHANUMERIC, which is the actual password quality for
-                    // unified lock.
-                    final long quality = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
-                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id);
-                    if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                        // Only possible when it's upgraded from nyc dp3
-                        Slog.i(TAG, "Migrated tied profile lock type");
-                        setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
-                                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, userInfo.id);
-                    } else if (quality != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) {
-                        // It should not happen
-                        Slog.e(TAG, "Invalid tied profile lock type: " + quality);
-                    }
-                }
+                // Migrate owner info enabled. Note there was a bug where older platforms only
+                // stored this value if the checkbox was toggled at least once. The code detects
+                // this case by handling the exception.
+                final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+                boolean enabled;
                 try {
-                    final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id;
-                    java.security.KeyStore keyStore =
-                            java.security.KeyStore.getInstance("AndroidKeyStore");
-                    keyStore.load(null);
-                    if (keyStore.containsAlias(alias)) {
-                        keyStore.deleteEntry(alias);
+                    int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
+                    enabled = ivalue != 0;
+                    setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
+                } catch (SettingNotFoundException e) {
+                    // Setting was never stored. Store it if the string is not empty.
+                    if (!TextUtils.isEmpty(ownerInfo)) {
+                        setLong(OWNER_INFO_ENABLED, 1, userId);
                     }
-                } catch (KeyStoreException | NoSuchAlgorithmException |
-                        CertificateException | IOException e) {
-                    Slog.e(TAG, "Unable to remove tied profile key", e);
+                }
+                Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
+            }
+            // No need to move the password / pattern files. They're already in the right place.
+            setString("migrated_user_specific", "true", 0);
+            Slog.i(TAG, "Migrated per-user lock settings to new location");
+        }
+
+        // Migrates biometric weak such that the fallback mechanism becomes the primary.
+        if (getString("migrated_biometric_weak", null, 0) == null) {
+            List<UserInfo> users = mUserManager.getUsers();
+            for (int i = 0; i < users.size(); i++) {
+                int userId = users.get(i).id;
+                long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                        userId);
+                long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                        userId);
+                if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
+                    setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
+                            alternateType,
+                            userId);
+                }
+                setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                        userId);
+            }
+            setString("migrated_biometric_weak", "true", 0);
+            Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
+        }
+
+        // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
+        // user was present on the system, so if we're upgrading to M and there is more than one
+        // user we disable the flag to remain consistent.
+        if (getString("migrated_lockscreen_disabled", null, 0) == null) {
+            final List<UserInfo> users = mUserManager.getUsers();
+            final int userCount = users.size();
+            int switchableUsers = 0;
+            for (int i = 0; i < userCount; i++) {
+                if (users.get(i).supportsSwitchTo()) {
+                    switchableUsers++;
                 }
             }
 
-            boolean isWatch = mContext.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WATCH);
-            // Wear used to set DISABLE_LOCKSCREEN to 'true', but because Wear now allows accounts
-            // and device management the lockscreen must be re-enabled now for users that upgrade.
-            if (isWatch && getString("migrated_wear_lockscreen_disabled", null, 0) == null) {
-                final int userCount = users.size();
+            if (switchableUsers > 1) {
                 for (int i = 0; i < userCount; i++) {
                     int id = users.get(i).id;
-                    setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+
+                    if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
+                        setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+                    }
                 }
-                setString("migrated_wear_lockscreen_disabled", "true", 0);
-                Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices");
             }
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to migrate old data", re);
+
+            setString("migrated_lockscreen_disabled", "true", 0);
+            Slog.i(TAG, "Migrated lockscreen disabled flag");
+        }
+
+        final List<UserInfo> users = mUserManager.getUsers();
+        for (int i = 0; i < users.size(); i++) {
+            final UserInfo userInfo = users.get(i);
+            if (userInfo.isManagedProfile() && mStorage.hasChildProfileLock(userInfo.id)) {
+                // When managed profile has a unified lock, the password quality stored has 2
+                // possibilities only.
+                // 1). PASSWORD_QUALITY_UNSPECIFIED, which is upgraded from dp2, and we are
+                // going to set it back to PASSWORD_QUALITY_ALPHANUMERIC.
+                // 2). PASSWORD_QUALITY_ALPHANUMERIC, which is the actual password quality for
+                // unified lock.
+                final long quality = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id);
+                if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                    // Only possible when it's upgraded from nyc dp3
+                    Slog.i(TAG, "Migrated tied profile lock type");
+                    setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
+                            DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, userInfo.id);
+                } else if (quality != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) {
+                    // It should not happen
+                    Slog.e(TAG, "Invalid tied profile lock type: " + quality);
+                }
+            }
+            try {
+                final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id;
+                java.security.KeyStore keyStore =
+                        java.security.KeyStore.getInstance("AndroidKeyStore");
+                keyStore.load(null);
+                if (keyStore.containsAlias(alias)) {
+                    keyStore.deleteEntry(alias);
+                }
+            } catch (KeyStoreException | NoSuchAlgorithmException |
+                    CertificateException | IOException e) {
+                Slog.e(TAG, "Unable to remove tied profile key", e);
+            }
+        }
+
+        boolean isWatch = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WATCH);
+        // Wear used to set DISABLE_LOCKSCREEN to 'true', but because Wear now allows accounts
+        // and device management the lockscreen must be re-enabled now for users that upgrade.
+        if (isWatch && getString("migrated_wear_lockscreen_disabled", null, 0) == null) {
+            final int userCount = users.size();
+            for (int i = 0; i < userCount; i++) {
+                int id = users.get(i).id;
+                setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+            }
+            setString("migrated_wear_lockscreen_disabled", "true", 0);
+            Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices");
         }
     }
 
@@ -868,7 +864,7 @@
     }
 
     @Override
-    public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException {
+    public boolean getSeparateProfileChallengeEnabled(int userId) {
         checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId);
         synchronized (mSeparateChallengeLock) {
             return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
@@ -877,7 +873,7 @@
 
     @Override
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
-            String managedUserPassword) throws RemoteException {
+            String managedUserPassword) {
         checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
             setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
@@ -891,19 +887,19 @@
     }
 
     @Override
-    public void setBoolean(String key, boolean value, int userId) throws RemoteException {
+    public void setBoolean(String key, boolean value, int userId) {
         checkWritePermission(userId);
         setStringUnchecked(key, userId, value ? "1" : "0");
     }
 
     @Override
-    public void setLong(String key, long value, int userId) throws RemoteException {
+    public void setLong(String key, long value, int userId) {
         checkWritePermission(userId);
         setStringUnchecked(key, userId, Long.toString(value));
     }
 
     @Override
-    public void setString(String key, String value, int userId) throws RemoteException {
+    public void setString(String key, String value, int userId) {
         checkWritePermission(userId);
         setStringUnchecked(key, userId, value);
     }
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index be223f1..d71c3b0 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -16,7 +16,7 @@
 
 package com.android.server.media;
 
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.media.AudioPlaybackConfiguration;
 import android.media.IAudioService;
@@ -27,14 +27,15 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.HashMap;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -49,47 +50,57 @@
     private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();
 
     /**
-     * Called when the state of audio player is changed.
+     * Listener for handling the active state changes of audio players.
      */
-    interface OnAudioPlayerStateChangedListener {
-        void onAudioPlayerStateChanged(
-                int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
+    interface OnAudioPlayerActiveStateChangedListener {
+        /**
+         * Called when the active state of audio player is changed.
+         *
+         * @param config The audio playback configuration for the audio player of which active state
+         *              was changed. If {@param isRemoved} is {@code true}, this hold outdated
+         *              information.
+         * @param isRemoved {@code true} if the audio player is removed.
+         */
+        void onAudioPlayerActiveStateChanged(
+                @NonNull AudioPlaybackConfiguration config, boolean isRemoved);
     }
 
     private final static class MessageHandler extends Handler {
-        private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1;
+        private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;
 
-        private final OnAudioPlayerStateChangedListener mListsner;
+        private final OnAudioPlayerActiveStateChangedListener mListener;
 
-        public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
+        public MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {
             super(looper);
-            mListsner = listener;
+            mListener = listener;
         }
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_AUDIO_PLAYER_STATE_CHANGED:
-                    mListsner.onAudioPlayerStateChanged(
-                            msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj);
+                case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:
+                    mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,
+                            msg.arg1 != 0);
                     break;
             }
         }
 
-        public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
-                AudioPlaybackConfiguration config) {
-            obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
+        public void sendAudioPlayerActiveStateChangedMessage(
+                final AudioPlaybackConfiguration config, final boolean isRemoved) {
+            obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,
+                    isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();
         }
     }
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
-    private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
-            new HashMap<>();
+    private final Map<OnAudioPlayerActiveStateChangedListener, MessageHandler> mListenerMap =
+            new ArrayMap<>();
     @GuardedBy("mLock")
-    private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
+    private final Set<Integer> mActiveAudioUids = new ArraySet();
     @GuardedBy("mLock")
-    private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
+    private ArrayMap<Integer, AudioPlaybackConfiguration> mPrevActiveAudioPlaybackConfigs =
+            new ArrayMap<>();
     // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
     // The UID whose audio playback becomes active at the last comes first.
     // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@@ -122,32 +133,24 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates);
-            final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid =
-                    new HashMap<>(mAudioPlayersForUid);
             synchronized (mLock) {
-                mAudioPlayerStates.clear();
-                mAudioPlayersForUid.clear();
+                // Update mActiveAudioUids
+                mActiveAudioUids.clear();
+                ArrayMap<Integer, AudioPlaybackConfiguration> activeAudioPlaybackConfigs =
+                        new ArrayMap<>();
                 for (AudioPlaybackConfiguration config : configs) {
-                    int pii = config.getPlayerInterfaceId();
-                    int uid = config.getClientUid();
-                    mAudioPlayerStates.put(pii, config.getPlayerState());
-                    HashSet<Integer> players = mAudioPlayersForUid.get(uid);
-                    if (players == null) {
-                        players = new HashSet<Integer>();
-                        players.add(pii);
-                        mAudioPlayersForUid.put(uid, players);
-                    } else {
-                        players.add(pii);
+                    if (config.isActive()) {
+                        mActiveAudioUids.add(config.getClientUid());
+                        activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);
                     }
                 }
-                for (AudioPlaybackConfiguration config : configs) {
-                    if (!config.isActive()) {
-                        continue;
-                    }
 
-                    int uid = config.getClientUid();
-                    if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
+                // Update mSortedAuioPlaybackClientUids.
+                for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {
+                    AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);
+                    final int uid = config.getClientUid();
+                    if (!mPrevActiveAudioPlaybackConfigs.containsKey(
+                            config.getPlayerInterfaceId())) {
                         if (DEBUG) {
                             Log.d(TAG, "Found a new active media playback. " +
                                     AudioPlaybackConfiguration.toLogFriendlyString(config));
@@ -163,40 +166,21 @@
                         mSortedAudioPlaybackClientUids.add(0, uid);
                     }
                 }
-                // Notify the change of audio player states.
+                // Notify the active state change of audio players.
                 for (AudioPlaybackConfiguration config : configs) {
-                    final Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
-                    final int prevStateInt =
-                            (prevState == null) ? AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN :
-                                prevState.intValue();
-                    if (prevStateInt != config.getPlayerState()) {
-                        sendAudioPlayerStateChangedMessageLocked(
-                                config.getClientUid(), prevStateInt, config);
+                    final int pii = config.getPlayerInterfaceId();
+                    boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;
+                    if (wasActive != config.isActive()) {
+                        sendAudioPlayerActiveStateChangedMessageLocked(
+                                config, /* isRemoved */ false);
                     }
                 }
-                for (Integer prevUid : prevAudioPlayersForUid.keySet()) {
-                    // If all players for prevUid is removed, notify the prev state was
-                    // PLAYER_STATE_STARTED only when there were a player whose state was
-                    // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify.
-                    if (!mAudioPlayersForUid.containsKey(prevUid)) {
-                        Set<Integer> prevPlayers = prevAudioPlayersForUid.get(prevUid);
-                        int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
-                        for (int pii : prevPlayers) {
-                            Integer state = prevAudioPlayerStates.get(pii);
-                            if (state == null) {
-                                continue;
-                            }
-                            if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
-                                prevState = state;
-                                break;
-                            } else if (prevState
-                                    == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
-                                prevState = state;
-                            }
-                        }
-                        sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
-                    }
+                for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {
+                    sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);
                 }
+
+                // Update mPrevActiveAudioPlaybackConfigs
+                mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -204,9 +188,10 @@
     }
 
     /**
-     * Registers OnAudioPlayerStateChangedListener.
+     * Registers OnAudioPlayerActiveStateChangedListener.
      */
-    public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
+    public void registerListener(
+            OnAudioPlayerActiveStateChangedListener listener, Handler handler) {
         synchronized (mLock) {
             mListenerMap.put(listener, new MessageHandler((handler == null) ?
                     Looper.myLooper() : handler.getLooper(), listener));
@@ -214,9 +199,9 @@
     }
 
     /**
-     * Unregisters OnAudioPlayerStateChangedListener.
+     * Unregisters OnAudioPlayerActiveStateChangedListener.
      */
-    public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
+    public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {
         synchronized (mLock) {
             mListenerMap.remove(listener);
         }
@@ -239,16 +224,7 @@
      */
     public boolean isPlaybackActive(int uid) {
         synchronized (mLock) {
-            Set<Integer> players = mAudioPlayersForUid.get(uid);
-            if (players == null) {
-                return false;
-            }
-            for (Integer pii : players) {
-                if (isActiveState(mAudioPlayerStates.get(pii))) {
-                    return true;
-                }
-            }
-            return false;
+            return mActiveAudioUids.contains(uid);
         }
     }
 
@@ -314,14 +290,10 @@
         }
     }
 
-    private void sendAudioPlayerStateChangedMessageLocked(
-            final int uid, final int prevState, final AudioPlaybackConfiguration config) {
+    private void sendAudioPlayerActiveStateChangedMessageLocked(
+            final AudioPlaybackConfiguration config, final boolean isRemoved) {
         for (MessageHandler messageHandler : mListenerMap.values()) {
-            messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config);
+            messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);
         }
     }
-
-    private static boolean isActiveState(Integer state) {
-        return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
-    }
 }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3c9e1d4..3e51252 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -19,7 +19,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.server.Watchdog;
 
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -101,6 +101,8 @@
     private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
     private final Handler mHandler = new Handler();
     private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
+    private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
+    private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
 
     public MediaRouterService(Context context) {
         mContext = context;
@@ -111,7 +113,7 @@
 
         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
         mAudioPlayerStateMonitor.registerListener(
-                new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
+                new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() {
             static final long WAIT_MS = 500;
             final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
                 @Override
@@ -121,39 +123,41 @@
             };
 
             @Override
-            public void onAudioPlayerStateChanged(
-                    int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
+            public void onAudioPlayerActiveStateChanged(
+                    @NonNull AudioPlaybackConfiguration config, boolean isRemoved) {
+                final boolean active = !isRemoved && config.isActive();
+                final int pii = config.getPlayerInterfaceId();
+                final int uid = config.getClientUid();
+
+                final int idx = mActivePlayerMinPriorityQueue.indexOf(pii);
+                // Keep the latest active player and its uid at the end of the queue.
+                if (idx >= 0) {
+                    mActivePlayerMinPriorityQueue.remove(idx);
+                    mActivePlayerUidMinPriorityQueue.remove(idx);
+                }
+
                 int restoreUid = -1;
-                boolean active = config == null ? false : config.isActive();
                 if (active) {
+                    mActivePlayerMinPriorityQueue.add(config.getPlayerInterfaceId());
+                    mActivePlayerUidMinPriorityQueue.add(uid);
                     restoreUid = uid;
-                } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
-                    // Noting to do if the prev state is not an active state.
-                    return;
-                } else {
-                    IntArray sortedAudioPlaybackClientUids =
-                            mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
-                    for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
-                        if (mAudioPlayerStateMonitor.isPlaybackActive(
-                                sortedAudioPlaybackClientUids.get(i))) {
-                            restoreUid = sortedAudioPlaybackClientUids.get(i);
-                            break;
-                        }
-                    }
+                } else if (mActivePlayerUidMinPriorityQueue.size() > 0) {
+                    restoreUid = mActivePlayerUidMinPriorityQueue.get(
+                            mActivePlayerUidMinPriorityQueue.size() - 1);
                 }
 
                 mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
                 if (restoreUid >= 0) {
                     restoreRoute(restoreUid);
                     if (DEBUG) {
-                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
-                                + " active " + active + " restoring " + restoreUid);
+                        Slog.d(TAG, "onAudioPlayerActiveStateChanged: " + "uid=" + uid
+                                + ", active=" + active + ", restoreUid=" + restoreUid);
                     }
                 } else {
                     mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
                     if (DEBUG) {
-                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
-                                + " active " + active + " delaying");
+                        Slog.d(TAG, "onAudioPlayerACTIVEStateChanged: " + "uid=" + uid
+                                + ", active=" + active + ", delaying");
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f6a81d0..06f4f5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.media;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
@@ -138,23 +137,19 @@
         mAudioService = getAudioService();
         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
         mAudioPlayerStateMonitor.registerListener(
-                new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
-            @Override
-            public void onAudioPlayerStateChanged(
-                    int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
-                if (config == null || !config.isActive() || config.getPlayerType()
-                        == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
-                    return;
-                }
-                synchronized (mLock) {
-                    FullUserRecord user =
-                            getFullUserRecordLocked(UserHandle.getUserId(uid));
-                    if (user != null) {
-                        user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+                (config, isRemoved) -> {
+                    if (isRemoved || !config.isActive() || config.getPlayerType()
+                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                        return;
                     }
-                }
-            }
-        }, null /* handler */);
+                    synchronized (mLock) {
+                        FullUserRecord user = getFullUserRecordLocked(
+                                UserHandle.getUserId(config.getClientUid()));
+                        if (user != null) {
+                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+                        }
+                    }
+                }, null /* handler */);
         mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
         mContentResolver = getContext().getContentResolver();
         mSettingsObserver = new SettingsObserver();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index eb94414..6ba1d8d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -771,7 +771,7 @@
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
                     }
-                    if (expanded) {
+                    if (expanded && userAction) {
                         r.recordExpanded();
                     }
                     EventLogTags.writeNotificationExpansion(key,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9162a97..7748ae4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8062,19 +8062,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        switch (windowType) {
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
-            case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
                 && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index de4fd7cd..88d1e55 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -292,6 +292,8 @@
         public void setMagnificationSpecLocked(MagnificationSpec spec) {
             mMagnifedViewport.updateMagnificationSpecLocked(spec);
             mMagnifedViewport.recomputeBoundsLocked();
+
+            mService.applyMagnificationSpec(spec);
             mService.scheduleAnimationLocked();
         }
 
@@ -421,7 +423,7 @@
         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
             if (spec != null && !spec.isNop()) {
-                if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                if (!windowState.shouldMagnify()) {
                     return null;
                 }
             }
@@ -476,6 +478,7 @@
             private final ViewportWindow mWindow;
 
             private boolean mFullRedrawNeeded;
+            private int mTempLayer = 0;
 
             public MagnifiedViewport() {
                 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
@@ -565,7 +568,7 @@
                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
 
-                    if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                    if (windowState.shouldMagnify()) {
                         mMagnificationRegion.op(windowBounds, Region.Op.UNION);
                         mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
                     } else {
@@ -676,10 +679,12 @@
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
                 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+                mTempLayer = 0;
                 dc.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisibleLw()
                             && !w.mWinAnimator.mEnterAnimationPending) {
-                        outWindows.put(w.mLayer, w);
+                        mTempLayer++;
+                        outWindows.put(mTempLayer, w);
                     }
                 }, false /* traverseTopToBottom */ );
             }
@@ -705,7 +710,7 @@
                     SurfaceControl surfaceControl = null;
                     try {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+                        surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
                                 .setName(SURFACE_TITLE)
                                 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
@@ -714,8 +719,6 @@
                         /* ignore */
                     }
                     mSurfaceControl = surfaceControl;
-                    mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
-                            .getLayerStack());
                     mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
                             TYPE_MAGNIFICATION_OVERLAY)
                             * WindowManagerService.TYPE_LAYER_MULTIPLIER);
@@ -1005,6 +1008,8 @@
 
         private final long mRecurringAccessibilityEventsIntervalMillis;
 
+        private int mTempLayer = 0;
+
         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                 WindowsForAccessibilityCallback callback) {
             mContext = windowManagerService.mContext;
@@ -1090,6 +1095,7 @@
                     if (isReportedWindowType(windowState.mAttrs.type)) {
                         // Add the window to the ones to be reported.
                         WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+                        window.layer = addedWindows.size();
                         addedWindows.add(window.token);
                         windows.add(window);
                         if (windowState.isFocused()) {
@@ -1323,9 +1329,10 @@
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
             final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+            mTempLayer = 0;
             dc.forAllWindows((w) -> {
                 if (w.isVisibleLw()) {
-                    outWindows.put(w.mLayer, w);
+                    outWindows.put(mTempLayer++, w);
                 }
             }, false /* traverseTopToBottom */ );
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2ef7f25..accfc65 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -253,7 +253,6 @@
 
     private void updateLayers() {
         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
-        updateThumbnailLayer();
     }
 
     private void stepThumbnailAnimation(long currentTime) {
@@ -283,27 +282,12 @@
                 + "][" + tmpFloats[Matrix.MSKEW_X]
                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        updateThumbnailLayer();
         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
     }
 
     /**
-     * Updates the thumbnail layer z order to just above the highest animation layer if changed
-     */
-    void updateThumbnailLayer() {
-        if (thumbnail != null) {
-            final int layer = mAppToken.getHighestAnimLayer();
-            if (DEBUG_LAYERS) Slog.v(TAG,
-                    "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
-            thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-            mThumbnailLayer = layer;
-        }
-    }
-
-    /**
      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
      * and keep producing the first frame of the animation.
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9729e50..f19cd0f 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -30,7 +30,6 @@
 import android.util.Slog;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 /**
  * Four black surfaces put together to make a black frame.
@@ -42,22 +41,22 @@
         final int layer;
         final SurfaceControl surface;
 
-        BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
-                throws OutOfResourcesException {
+        BlackSurface(int layer,
+                int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
             left = l;
             top = t;
             this.layer = layer;
             int w = r-l;
             int h = b-t;
 
-            surface = new SurfaceControl.Builder(session)
+            surface = dc.makeOverlay()
                     .setName("BlackSurface")
                     .setSize(w, h)
                     .setColorLayer(true)
+                    .setParent(null) // TODO: Work-around for b/69259549
                     .build();
 
             surface.setAlpha(1);
-            surface.setLayerStack(layerStack);
             surface.setLayer(layer);
             surface.show();
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
@@ -114,30 +113,32 @@
         }
     }
 
-    public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+    public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
             boolean forceDefaultOrientation) throws OutOfResourcesException {
         boolean success = false;
 
         mForceDefaultOrientation = forceDefaultOrientation;
 
+        // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+        // b/68253229
         mOuterRect = new Rect(outer);
         mInnerRect = new Rect(inner);
         try {
             if (outer.top < inner.top) {
-                mBlackSurfaces[0] = new BlackSurface(session, layer,
-                        outer.left, outer.top, inner.right, inner.top, layerStack);
+                mBlackSurfaces[0] = new BlackSurface(layer,
+                        outer.left, outer.top, inner.right, inner.top, dc);
             }
             if (outer.left < inner.left) {
-                mBlackSurfaces[1] = new BlackSurface(session, layer,
-                        outer.left, inner.top, inner.left, outer.bottom, layerStack);
+                mBlackSurfaces[1] = new BlackSurface(layer,
+                        outer.left, inner.top, inner.left, outer.bottom, dc);
             }
             if (outer.bottom > inner.bottom) {
-                mBlackSurfaces[2] = new BlackSurface(session, layer,
-                        inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+                mBlackSurfaces[2] = new BlackSurface(layer,
+                        inner.left, inner.bottom, outer.right, outer.bottom, dc);
             }
             if (outer.right > inner.right) {
-                mBlackSurfaces[3] = new BlackSurface(session, layer,
-                        inner.right, outer.top, outer.right, inner.bottom, layerStack);
+                mBlackSurfaces[3] = new BlackSurface(layer,
+                        inner.right, outer.top, outer.right, inner.bottom, dc);
             }
             success = true;
         } finally {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2d5d1b2..2a216ab 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -33,7 +33,6 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class CircularDisplayMask {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
@@ -54,8 +53,10 @@
     private boolean mDimensionsUnequal = false;
     private int mMaskThickness;
 
-    public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+    public CircularDisplayMask(DisplayContent dc, int zOrder,
             int screenOffset, int maskThickness) {
+        final Display display = dc.getDisplay();
+
         mScreenSize = new Point();
         display.getSize(mScreenSize);
         if (mScreenSize.x != mScreenSize.y + screenOffset) {
@@ -66,7 +67,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("CircularDisplayMask")
                     .setSize(mScreenSize.x, mScreenSize.y) // not a typo
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index cc94807..b534b8a 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -375,6 +375,10 @@
         return toString();
     }
 
+    boolean isAlwaysOnTop() {
+        return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+    }
+
     abstract protected int getChildCount();
 
     abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
deleted file mode 100644
index 8fb2be8..0000000
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2014 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.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
-
-import java.io.PrintWriter;
-
-public class DimLayer {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
-    private final WindowManagerService mService;
-
-    /** Actual surface that dims */
-    private SurfaceControl mDimSurface;
-
-    /** Last value passed to mDimSurface.setAlpha() */
-    private float mAlpha = 0;
-
-    /** Last value passed to mDimSurface.setLayer() */
-    private int mLayer = -1;
-
-    /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
-    private final Rect mBounds = new Rect();
-
-    /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
-    private final Rect mLastBounds = new Rect();
-
-    /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
-    private boolean mShowing = false;
-
-    /** Value of mAlpha when beginning transition to mTargetAlpha */
-    private float mStartAlpha = 0;
-
-    /** Final value of mAlpha following transition */
-    private float mTargetAlpha = 0;
-
-    /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
-    private long mStartTime;
-
-    /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
-    private long mDuration;
-
-    private boolean mDestroyed = false;
-
-    private final int mDisplayId;
-
-
-    /** Interface implemented by users of the dim layer */
-    interface DimLayerUser {
-        /** Returns true if the  dim should be fullscreen. */
-        boolean dimFullscreen();
-        /** Returns the display info. of the dim layer user. */
-        DisplayInfo getDisplayInfo();
-        /** Returns true if the dim layer user is currently attached to a display */
-        boolean isAttachedToDisplay();
-        /** Gets the bounds of the dim layer user. */
-        void getDimBounds(Rect outBounds);
-        /** Returns the layer to place a dim layer. */
-        default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
-                int defaultLayer) {
-            return defaultLayer;
-        }
-
-        String toShortString();
-    }
-    /** The user of this dim layer. */
-    private final DimLayerUser mUser;
-
-    private final String mName;
-
-    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
-        mUser = user;
-        mDisplayId = displayId;
-        mService = service;
-        mName = name;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
-    }
-
-    private void constructSurface(WindowManagerService service) {
-        service.openSurfaceTransaction();
-        try {
-            mDimSurface = new SurfaceControl.Builder(service.mFxSession)
-                    .setName(mName)
-                    .setSize(16, 16)
-                    .setColorLayer(true)
-                    .build();
-
-            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
-                    "  DIM " + mDimSurface + ": CREATE");
-            mDimSurface.setLayerStack(mDisplayId);
-            adjustBounds();
-            adjustAlpha(mAlpha);
-            adjustLayer(mLayer);
-        } catch (Exception e) {
-            Slog.e(TAG_WM, "Exception creating Dim surface", e);
-        } finally {
-            service.closeSurfaceTransaction("DimLayer.constructSurface");
-        }
-    }
-
-    /** Return true if dim layer is showing */
-    boolean isDimming() {
-        return mTargetAlpha != 0;
-    }
-
-    /** Return true if in a transition period */
-    boolean isAnimating() {
-        return mTargetAlpha != mAlpha;
-    }
-
-    float getTargetAlpha() {
-        return mTargetAlpha;
-    }
-
-    void setLayer(int layer) {
-        if (mLayer == layer) {
-            return;
-        }
-        mLayer = layer;
-        adjustLayer(layer);
-    }
-
-    private void adjustLayer(int layer) {
-        if (mDimSurface != null) {
-            mDimSurface.setLayer(layer);
-        }
-    }
-
-    int getLayer() {
-        return mLayer;
-    }
-
-    private void setAlpha(float alpha) {
-        if (mAlpha == alpha) {
-            return;
-        }
-        mAlpha = alpha;
-        adjustAlpha(alpha);
-    }
-
-    private void adjustAlpha(float alpha) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
-        try {
-            if (mDimSurface != null) {
-                mDimSurface.setAlpha(alpha);
-            }
-            if (alpha == 0 && mShowing) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
-                if (mDimSurface != null) {
-                    mDimSurface.hide();
-                    mShowing = false;
-                }
-            } else if (alpha > 0 && !mShowing) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
-                if (mDimSurface != null) {
-                    mDimSurface.show();
-                    mShowing = true;
-                }
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure setting alpha immediately", e);
-        }
-    }
-
-    /**
-     * NOTE: Must be called with Surface transaction open.
-     */
-    private void adjustBounds() {
-        if (mUser.dimFullscreen()) {
-            getBoundsForFullscreen(mBounds);
-        }
-
-        if (mDimSurface != null) {
-            mDimSurface.setPosition(mBounds.left, mBounds.top);
-            mDimSurface.setSize(mBounds.width(), mBounds.height());
-            if (DEBUG_DIM_LAYER) Slog.v(TAG,
-                    "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
-        }
-
-        mLastBounds.set(mBounds);
-    }
-
-    private void getBoundsForFullscreen(Rect outBounds) {
-        final int dw, dh;
-        final float xPos, yPos;
-        // Set surface size to screen size.
-        final DisplayInfo info = mUser.getDisplayInfo();
-        // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
-        // a corner.
-        dw = (int) (info.logicalWidth * 1.5);
-        dh = (int) (info.logicalHeight * 1.5);
-        // back off position so 1/4 of Surface is before and 1/4 is after.
-        xPos = -1 * dw / 6;
-        yPos = -1 * dh / 6;
-        outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
-    }
-
-    void setBoundsForFullscreen() {
-        getBoundsForFullscreen(mBounds);
-        setBounds(mBounds);
-    }
-
-    /** @param bounds The new bounds to set */
-    void setBounds(Rect bounds) {
-        mBounds.set(bounds);
-        if (isDimming() && !mLastBounds.equals(bounds)) {
-            try {
-                mService.openSurfaceTransaction();
-                adjustBounds();
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Failure setting size", e);
-            } finally {
-                mService.closeSurfaceTransaction("DimLayer.setBounds");
-            }
-        }
-    }
-
-    /**
-     * @param duration The time to test.
-     * @return True if the duration would lead to an earlier end to the current animation.
-     */
-    private boolean durationEndsEarlier(long duration) {
-        return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
-    }
-
-    /** Jump to the end of the animation.
-     * NOTE: Must be called with Surface transaction open. */
-    void show() {
-        if (isAnimating()) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
-            show(mLayer, mTargetAlpha, 0);
-        }
-    }
-
-    /**
-     * Begin an animation to a new dim value.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @param layer The layer to set the surface to.
-     * @param alpha The dim value to end at.
-     * @param duration How long to take to get there in milliseconds.
-     */
-    void show(int layer, float alpha, long duration) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
-                + " duration=" + duration + ", mDestroyed=" + mDestroyed);
-        if (mDestroyed) {
-            Slog.e(TAG, "show: no Surface");
-            // Make sure isAnimating() returns false.
-            mTargetAlpha = mAlpha = 0;
-            return;
-        }
-
-        if (mDimSurface == null) {
-            constructSurface(mService);
-        }
-
-        if (!mLastBounds.equals(mBounds)) {
-            adjustBounds();
-        }
-        setLayer(layer);
-
-        long curTime = SystemClock.uptimeMillis();
-        final boolean animating = isAnimating();
-        if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
-                || (!animating && mAlpha != alpha)) {
-            if (duration <= 0) {
-                // No animation required, just set values.
-                setAlpha(alpha);
-            } else {
-                // Start or continue animation with new parameters.
-                mStartAlpha = mAlpha;
-                mStartTime = curTime;
-                mDuration = duration;
-            }
-        }
-        mTargetAlpha = alpha;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
-                + mStartTime + " mTargetAlpha=" + mTargetAlpha);
-    }
-
-    /** Immediate hide.
-     * NOTE: Must be called with Surface transaction open. */
-    void hide() {
-        if (mShowing) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
-            hide(0);
-        }
-    }
-
-    /**
-     * Gradually fade to transparent.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @param duration Time to fade in milliseconds.
-     */
-    void hide(long duration) {
-        if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
-            show(mLayer, 0, duration);
-        }
-    }
-
-    /**
-     * Advance the dimming per the last #show(int, float, long) call.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @return True if animation is still required after this step.
-     */
-    boolean stepAnimation() {
-        if (mDestroyed) {
-            Slog.e(TAG, "stepAnimation: surface destroyed");
-            // Ensure that isAnimating() returns false;
-            mTargetAlpha = mAlpha = 0;
-            return false;
-        }
-        if (isAnimating()) {
-            final long curTime = SystemClock.uptimeMillis();
-            final float alphaDelta = mTargetAlpha - mStartAlpha;
-            float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
-            if (alphaDelta > 0 && alpha > mTargetAlpha ||
-                    alphaDelta < 0 && alpha < mTargetAlpha) {
-                // Don't exceed limits.
-                alpha = mTargetAlpha;
-            }
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
-            setAlpha(alpha);
-        }
-
-        return isAnimating();
-    }
-
-    /** Cleanup */
-    void destroySurface() {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
-        if (mDimSurface != null) {
-            mDimSurface.destroy();
-            mDimSurface = null;
-        }
-        mDestroyed = true;
-    }
-
-    public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
-                pw.print(" mLayer="); pw.print(mLayer);
-                pw.print(" mAlpha="); pw.println(mAlpha);
-        pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
-                pw.print(" mBounds="); pw.println(mBounds.toShortString());
-        pw.print(prefix); pw.print("Last animation: ");
-                pw.print(" mDuration="); pw.print(mDuration);
-                pw.print(" mStartTime="); pw.print(mStartTime);
-                pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
-        pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
-                pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
deleted file mode 100644
index 6f9e45a..0000000
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ /dev/null
@@ -1,403 +0,0 @@
-package com.android.server.wm;
-
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- * as well as other use cases (such as dimming above a dead window).
- */
-class DimLayerController {
-    private static final String TAG_LOCAL = "DimLayerController";
-    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
-    /** Amount of time in milliseconds to animate the dim surface from one value to another,
-     * when no window animation is driving it. */
-    private static final int DEFAULT_DIM_DURATION = 200;
-
-    /**
-     * The default amount of dim applied over a dead window
-     */
-    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
-
-    // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
-    // instead of creating a new object per fullscreen task on a display.
-    private DimLayer mSharedFullScreenDimLayer;
-
-    private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
-
-    private DisplayContent mDisplayContent;
-
-    private Rect mTmpBounds = new Rect();
-
-    DimLayerController(DisplayContent displayContent) {
-        mDisplayContent = displayContent;
-    }
-
-    /** Updates the dim layer bounds, recreating it if needed. */
-    void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
-        final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
-        final boolean previousFullscreen = state.dimLayer != null
-                && state.dimLayer == mSharedFullScreenDimLayer;
-        DimLayer newDimLayer;
-        final int displayId = mDisplayContent.getDisplayId();
-        if (dimLayerUser.dimFullscreen()) {
-            if (previousFullscreen && mSharedFullScreenDimLayer != null) {
-                // Update the bounds for fullscreen in case of rotation.
-                mSharedFullScreenDimLayer.setBoundsForFullscreen();
-                return;
-            }
-            // Use shared fullscreen dim layer
-            newDimLayer = mSharedFullScreenDimLayer;
-            if (newDimLayer == null) {
-                if (state.dimLayer != null) {
-                    // Re-purpose the previous dim layer.
-                    newDimLayer = state.dimLayer;
-                } else {
-                    // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
-                            getDimLayerTag(dimLayerUser));
-                }
-                dimLayerUser.getDimBounds(mTmpBounds);
-                newDimLayer.setBounds(mTmpBounds);
-                mSharedFullScreenDimLayer = newDimLayer;
-            } else if (state.dimLayer != null) {
-                state.dimLayer.destroySurface();
-            }
-        } else {
-            newDimLayer = (state.dimLayer == null || previousFullscreen)
-                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
-                            getDimLayerTag(dimLayerUser))
-                    : state.dimLayer;
-            dimLayerUser.getDimBounds(mTmpBounds);
-            newDimLayer.setBounds(mTmpBounds);
-        }
-        state.dimLayer = newDimLayer;
-    }
-
-    private static String getDimLayerTag(DimLayerUser dimLayerUser) {
-        return TAG_LOCAL + "/" + dimLayerUser.toShortString();
-    }
-
-    private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
-                + dimLayerUser.toShortString());
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state == null) {
-            state = new DimLayerState();
-            mState.put(dimLayerUser, state);
-        }
-        return state;
-    }
-
-    private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state == null) {
-            if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
-                    + dimLayerUser.toShortString());
-            return;
-        }
-        state.continueDimming = true;
-    }
-
-    boolean isDimming() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayerState state = mState.valueAt(i);
-            if (state.dimLayer != null && state.dimLayer.isDimming()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void resetDimming() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            mState.valueAt(i).continueDimming = false;
-        }
-    }
-
-    private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        return state != null && state.continueDimming;
-    }
-
-    void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
-            WindowStateAnimator newWinAnimator, boolean aboveApp) {
-        // Only set dim params on the highest dimmed layer.
-        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
-        DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
-        state.dimAbove = aboveApp;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " newWinAnimator=" + newWinAnimator
-                + " state.animator=" + state.animator);
-        if (newWinAnimator.getShown() && (state.animator == null
-                || !state.animator.getShown()
-                || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
-            state.animator = newWinAnimator;
-            if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
-                // Dim should cover the entire screen for system windows.
-                mDisplayContent.getLogicalDisplayRect(mTmpBounds);
-            } else {
-                dimLayerUser.getDimBounds(mTmpBounds);
-            }
-            state.dimLayer.setBounds(mTmpBounds);
-        }
-    }
-
-    void stopDimmingIfNeeded() {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
-            stopDimmingIfNeeded(dimLayerUser);
-        }
-    }
-
-    private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
-        // No need to check if state is null, we know the key has a value.
-        DimLayerState state = mState.get(dimLayerUser);
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " state.continueDimming=" + state.continueDimming
-                + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
-        if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
-            return;
-        }
-
-        if (!state.continueDimming && state.dimLayer.isDimming()) {
-            state.animator = null;
-            dimLayerUser.getDimBounds(mTmpBounds);
-            state.dimLayer.setBounds(mTmpBounds);
-        }
-    }
-
-    boolean animateDimLayers() {
-        int fullScreen = -1;
-        int fullScreenAndDimming = -1;
-        int topFullScreenUserLayer = 0;
-        boolean result = false;
-
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            final DimLayer.DimLayerUser user = mState.keyAt(i);
-            final DimLayerState state = mState.valueAt(i);
-
-            if (!user.isAttachedToDisplay()) {
-                // Leaked dim user that is no longer attached to the display. Go ahead and clean it
-                // clean-up and log what happened.
-                // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
-                // it self when it was detached from the display. Need to investigate how the dim
-                // user is leaking...
-                //Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
-                //        + " state=" + state);
-                Slog.w(TAG_WM, "Leaked dim user=" + user.toShortString() + " state=" + state);
-                removeDimLayerUser(user);
-                continue;
-            }
-
-            // We have to check that we are actually the shared fullscreen layer
-            // for this path. If we began as non fullscreen and became fullscreen
-            // (e.g. Docked stack closing), then we may not be the shared layer
-            // and we have to make sure we always animate the layer.
-            if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
-                fullScreen = i;
-                if (!state.continueDimming) {
-                    continue;
-                }
-
-                // When choosing which user to assign the shared fullscreen layer to
-                // we need to look at Z-order.
-                if (topFullScreenUserLayer == 0 ||
-                        (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
-                    fullScreenAndDimming = i;
-                    if (state.animator != null) {
-                        topFullScreenUserLayer = state.animator.mAnimLayer;
-                    }
-                }
-            } else {
-                // We always want to animate the non fullscreen windows, they don't share their
-                // dim layers.
-                result |= animateDimLayers(user);
-            }
-        }
-        // For the shared, full screen dim layer, we prefer the animation that is causing it to
-        // appear.
-        if (fullScreenAndDimming != -1) {
-            result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
-        } else if (fullScreen != -1) {
-            // If there is no animation for the full screen dim layer to appear, we can use any of
-            // the animators that will cause it to disappear.
-            result |= animateDimLayers(mState.keyAt(fullScreen));
-        }
-        return result;
-    }
-
-    private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " state.animator=" + state.animator
-                + " state.continueDimming=" + state.continueDimming);
-        final int dimLayer;
-        final float dimAmount;
-        if (state.animator == null) {
-            dimLayer = state.dimLayer.getLayer();
-            dimAmount = 0;
-        } else {
-            if (state.dimAbove) {
-                dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
-                dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
-            } else {
-                dimLayer = dimLayerUser.getLayerForDim(state.animator, LAYER_OFFSET_DIM,
-                        state.animator.mAnimLayer - LAYER_OFFSET_DIM);
-                dimAmount = state.animator.mWin.mAttrs.dimAmount;
-            }
-        }
-        final float targetAlpha = state.dimLayer.getTargetAlpha();
-        if (targetAlpha != dimAmount) {
-            if (state.animator == null) {
-                state.dimLayer.hide(DEFAULT_DIM_DURATION);
-            } else {
-                long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
-                        ? state.animator.mAnimation.computeDurationHint()
-                        : DEFAULT_DIM_DURATION;
-                if (targetAlpha > dimAmount) {
-                    duration = getDimLayerFadeDuration(duration);
-                }
-                state.dimLayer.show(dimLayer, dimAmount, duration);
-
-                // If we showed a dim layer, make sure to redo the layout because some things depend
-                // on whether a dim layer is showing or not.
-                if (targetAlpha == 0) {
-                    mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                    mDisplayContent.setLayoutNeeded();
-                }
-            }
-        } else if (state.dimLayer.getLayer() != dimLayer) {
-            state.dimLayer.setLayer(dimLayer);
-        }
-        if (state.dimLayer.isAnimating()) {
-            if (!mDisplayContent.okToAnimate()) {
-                // Jump to the end of the animation.
-                state.dimLayer.show();
-            } else {
-                return state.dimLayer.stepAnimation();
-            }
-        }
-        return false;
-    }
-
-    boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
-        DimLayerState state = mState.get(dimLayerUser);
-        return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
-    }
-
-    private long getDimLayerFadeDuration(long duration) {
-        TypedValue tv = new TypedValue();
-        mDisplayContent.mService.mContext.getResources().getValue(
-                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
-        if (tv.type == TypedValue.TYPE_FRACTION) {
-            duration = (long) tv.getFraction(duration, duration);
-        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
-            duration = tv.data;
-        }
-        return duration;
-    }
-
-    void close() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayerState state = mState.valueAt(i);
-            state.dimLayer.destroySurface();
-        }
-        mState.clear();
-        mSharedFullScreenDimLayer = null;
-    }
-
-    void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state != null) {
-            // Destroy the surface, unless it's the shared fullscreen dim.
-            if (state.dimLayer != mSharedFullScreenDimLayer) {
-                state.dimLayer.destroySurface();
-            }
-            mState.remove(dimLayerUser);
-        }
-        if (mState.isEmpty()) {
-            mSharedFullScreenDimLayer = null;
-        }
-    }
-
-    @VisibleForTesting
-    boolean hasDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
-        return mState.containsKey(dimLayerUser);
-    }
-
-    @VisibleForTesting
-    boolean hasSharedFullScreenDimLayer() {
-        return mSharedFullScreenDimLayer != null;
-    }
-
-    void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
-        applyDim(dimLayerUser, animator, false /* aboveApp */);
-    }
-
-    void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
-        applyDim(dimLayerUser, animator, true /* aboveApp */);
-    }
-
-    void applyDim(
-            DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
-        if (dimLayerUser == null) {
-            Slog.e(TAG, "Trying to apply dim layer for: " + this
-                    + ", but no dim layer user found.");
-            return;
-        }
-        if (!getContinueDimming(dimLayerUser)) {
-            setContinueDimming(dimLayerUser);
-            if (!isDimming(dimLayerUser, animator)) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
-                startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
-            }
-        }
-    }
-
-    private static class DimLayerState {
-        // The particular window requesting a dim layer. If null, hide dimLayer.
-        WindowStateAnimator animator;
-        // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
-        // end then stop any dimming.
-        boolean continueDimming;
-        DimLayer dimLayer;
-        boolean dimAbove;
-    }
-
-    void dump(String prefix, PrintWriter pw) {
-        pw.println(prefix + "DimLayerController");
-        final String doubleSpace = "  ";
-        final String prefixPlusDoubleSpace = prefix + doubleSpace;
-
-        for (int i = 0, n = mState.size(); i < n; i++) {
-            pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
-            DimLayerState state = mState.valueAt(i);
-            pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
-                    + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
-                    + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
-            if (state.dimLayer != null) {
-                state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
new file mode 100644
index 0000000..9fe16ae
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -0,0 +1,195 @@
+/*
+ * 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.wm;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.graphics.Rect;
+
+/**
+ * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
+ * black layers of varying opacity at various Z-levels which create the effect of a Dim.
+ */
+class Dimmer {
+    private static final String TAG = "WindowManager";
+
+    private class DimState {
+        SurfaceControl mSurfaceControl;
+        boolean mDimming;
+
+        /**
+         * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+         * details on Dim lifecycle.
+         */
+        boolean mDontReset;
+
+        DimState(SurfaceControl ctl) {
+            mSurfaceControl = ctl;
+            mDimming = true;
+        }
+    };
+
+    private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+
+    /**
+     * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+     * host, some controller of it, or one of the hosts children.
+     */
+    private WindowContainer mHost;
+
+    Dimmer(WindowContainer host) {
+        mHost = host;
+    }
+
+    SurfaceControl makeDimLayer() {
+        final SurfaceControl control = mHost.makeChildSurface(null)
+                .setParent(mHost.getSurfaceControl())
+                .setColorLayer(true)
+                .setName("Dim Layer for - " + mHost.getName())
+                .build();
+        return control;
+    }
+
+    /**
+     * Retreive the DimState for a given child of the host.
+     */
+    DimState getDimState(WindowContainer container) {
+        DimState state = mDimLayerUsers.get(container);
+        if (state == null) {
+            final SurfaceControl ctl = makeDimLayer();
+            state = new DimState(ctl);
+            /**
+             * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
+             * via state resetting for Dim's with containers.
+             */
+            if (container == null) {
+                state.mDontReset = true;
+            }
+            mDimLayerUsers.put(container, state);
+        }
+        return state;
+    }
+
+    private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
+            float alpha) {
+        final DimState d = getDimState(container);
+        t.show(d.mSurfaceControl);
+        if (container != null) {
+            t.setRelativeLayer(d.mSurfaceControl,
+                    container.getSurfaceControl(), relativeLayer);
+        } else {
+            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+        }
+        t.setAlpha(d.mSurfaceControl, alpha);
+
+        d.mDimming = true;
+    }
+
+    /**
+     * Finish a dim started by dimAbove in the case there was no call to dimAbove.
+     *
+     * @param t A Transaction in which to finish the dim.
+     */
+    void stopDim(SurfaceControl.Transaction t) {
+        DimState d = getDimState(null);
+        t.hide(d.mSurfaceControl);
+        d.mDontReset = false;
+    }
+    /**
+     * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
+     * remove this effect. If the Dim can be assosciated with a particular child of the host
+     * consider using the other variant of dimAbove which ties the Dim lifetime to the child
+     * lifetime more explicitly.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param alpha The alpha at which to Dim.
+     */
+    void dimAbove(SurfaceControl.Transaction t, float alpha) {
+        dim(t, null, 1, alpha);
+    }
+
+    /**
+     * Place a dim above the given container, which should be a child of the host container.
+     * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
+     * and the child should call dimAbove again to request the Dim to continue.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param container The container which to dim above. Should be a child of our host.
+     * @param alpha The alpha at which to Dim.
+     */
+    void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+        dim(t, container, 1, alpha);
+    }
+
+    /**
+     * Like {@link #dimAbove} but places the dim below the given container.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param container The container which to dim below. Should be a child of our host.
+     * @param alpha The alpha at which to Dim.
+     */
+
+    void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+        dim(t, container, -1, alpha);
+    }
+
+    /**
+     * Mark all dims as pending completion on the next call to {@link #updateDims}
+     *
+     * This is intended for us by the host container, to be called at the beginning of
+     * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+     * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
+     * a chance to request dims to continue.
+     */
+    void resetDimStates() {
+        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+            final DimState state = mDimLayerUsers.valueAt(i);
+            if (state.mDontReset == false) {
+                state.mDimming = false;
+            }
+        }
+    }
+
+    /**
+     * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
+     * described in {@link #resetDimStates}.
+     *
+     * @param t A transaction in which to update the dims.
+     * @param bounds The bounds at which to dim.
+     * @return true if any Dims were updated.
+     */
+    boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
+        boolean didSomething = false;
+        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+            DimState state = mDimLayerUsers.valueAt(i);
+            // TODO: We want to animate the addition and removal of Dim's instead of immediately
+            // acting. When we do this we need to take care to account for the "Replacing Windows"
+            // case (and seamless dim transfer).
+            if (state.mDimming == false) {
+                mDimLayerUsers.removeAt(i);
+                state.mSurfaceControl.destroy();
+            } else {
+                didSomething = true;
+                // TODO: Once we use geometry from hierarchy this falls away.
+                t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
+                t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+            }
+        }
+        return didSomething;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4d839d0..17312b2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -142,8 +142,10 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputDevice;
+import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -321,8 +323,6 @@
     final DockedStackDividerController mDividerControllerLocked;
     final PinnedStackController mPinnedStackControllerLocked;
 
-    DimLayerController mDimLayerController;
-
     final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
 
     private boolean mHaveBootMsg = false;
@@ -346,10 +346,37 @@
     // {@code false} if this display is in the processing of being created.
     private boolean mDisplayReady = false;
 
-    private final WindowLayersController mLayersController;
     WallpaperController mWallpaperController;
     int mInputMethodAnimLayerAdjustment;
 
+    private final SurfaceSession mSession = new SurfaceSession();
+
+    /**
+     * We organize all top-level Surfaces in to the following layers.
+     * mOverlayLayer contains a few Surfaces which are always on top of others
+     * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+     * {@link #mWindowingLayer} contains everything else.
+     */
+    private SurfaceControl mOverlayLayer;
+
+    /**
+     * See {@link #mOverlayLayer}
+     */
+    private SurfaceControl mWindowingLayer;
+
+    /**
+     * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+     * <p>
+     * For these surfaces currently we use a surface based on the larger of width or height so we
+     * don't have to resize when rotating the display.
+     */
+    private int mSurfaceSize;
+
+    /**
+     * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+     */
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         if (winAnimator.hasSurface()) {
@@ -503,9 +530,6 @@
         return true;
     };
 
-    private final Consumer<WindowState> mPrepareWindowSurfaces =
-            w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
     private final Consumer<WindowState> mPerformLayout = w -> {
         // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
         // wasting time and funky changes while a window is animating away.
@@ -558,12 +582,6 @@
                     w.updateLastInsetValues();
                 }
 
-                // Window frames may have changed. Update dim layer with the new bounds.
-                final Task task = w.getTask();
-                if (task != null) {
-                    mDimLayerController.updateDimLayer(task);
-                }
-
                 if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
                         + " mDisplayFrame=" + w.mDisplayFrame);
@@ -657,8 +675,6 @@
             }
         }
 
-        w.applyDimLayerIfNeeded();
-
         if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
                 && mWallpaperController.isWallpaperTarget(w)) {
             // This is the wallpaper target and its obscured state changed... make sure the
@@ -741,13 +757,11 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param layersController window layer controller used to assign layer to the windows on this
-     *                         display.
      * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
      *                            wallpaper windows in the window list.
      */
     DisplayContent(Display display, WindowManagerService service,
-            WindowLayersController layersController, WallpaperController wallpaperController) {
+            WallpaperController wallpaperController) {
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -756,7 +770,6 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mLayersController = layersController;
         mWallpaperController = wallpaperController;
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
@@ -766,7 +779,22 @@
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
-        mDimLayerController = new DimLayerController(this);
+
+        mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+                .setSize(mSurfaceSize, mSurfaceSize)
+                .setOpaque(true);
+        mWindowingLayer = b.setName("Display Root").build();
+        mOverlayLayer = b.setName("Display Overlays").build();
+
+        getPendingTransaction().setLayer(mWindowingLayer, 0)
+                .setLayerStack(mWindowingLayer, mDisplayId)
+                .show(mWindowingLayer)
+                .setLayer(mOverlayLayer, 1)
+                .setLayerStack(mOverlayLayer, mDisplayId)
+                .show(mOverlayLayer);
+        getPendingTransaction().apply();
 
         // These are the only direct children we should ever have and they are permanent.
         super.addChild(mBelowAppWindowsContainers, null);
@@ -1030,11 +1058,7 @@
 
         setLayoutNeeded();
         final int[] anim = new int[2];
-        if (isDimming()) {
-            anim[0] = anim[1] = 0;
-        } else {
-            mService.mPolicy.selectRotationAnimationLw(anim);
-        }
+        mService.mPolicy.selectRotationAnimationLw(anim);
 
         if (!rotateSeamlessly) {
             mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this);
@@ -1071,8 +1095,7 @@
             //       it doesn't support hardware OpenGL emulation yet.
             if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                     && screenRotationAnimation.hasScreenshot()) {
-                if (screenRotationAnimation.setRotationInTransaction(
-                        rotation, mService.mFxSession,
+                if (screenRotationAnimation.setRotationInTransaction(rotation,
                         MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
                         mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
                     mService.scheduleAnimationLocked();
@@ -1907,22 +1930,6 @@
         }
     }
 
-    boolean animateDimLayers() {
-        return mDimLayerController.animateDimLayers();
-    }
-
-    private void resetDimming() {
-        mDimLayerController.resetDimming();
-    }
-
-    boolean isDimming() {
-        return mDimLayerController.isDimming();
-    }
-
-    private void stopDimmingIfNeeded() {
-        mDimLayerController.stopDimmingIfNeeded();
-    }
-
     @Override
     void removeIfPossible() {
         if (isAnimating()) {
@@ -1938,7 +1945,6 @@
         try {
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
-            mDimLayerController.close();
             if (mService.canDispatchPointerEvents()) {
                 if (mTapDetector != null) {
                     mService.unregisterPointerEventListener(mTapDetector);
@@ -1947,6 +1953,9 @@
                     mService.unregisterPointerEventListener(mService.mMousePositionTracker);
                 }
             }
+            // The pending transaction won't be applied so we should
+            // just clean up any surfaces pending destruction.
+            onPendingTransactionApplied();
         } finally {
             mRemovingDisplay = false;
         }
@@ -2228,8 +2237,7 @@
                 token.dump(pw, "    ");
             }
         }
-        pw.println();
-        mDimLayerController.dump(prefix, pw);
+
         pw.println();
 
         // Dump stack references
@@ -2342,10 +2350,16 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        mLayersController.assignWindowLayers(this);
+        assignChildLayers(getPendingTransaction());
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
+
+        // We accumlate the layer changes in-to "getPendingTransaction()" but we defer
+        // the application of this transaction until the animation pass triggers
+        // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+        // the hiding and showing of surfaces.
+        scheduleAnimation();
     }
 
     // TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -2701,10 +2715,6 @@
         }
     }
 
-    void prepareWindowSurfaces() {
-        forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
-    }
-
     boolean inputMethodClientHasFocus(IInputMethodClient client) {
         final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
         if (imFocus == null) {
@@ -2846,7 +2856,6 @@
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
-        resetDimming();
 
         mTmpRecoveringMemory = recoveringMemory;
         forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -2857,8 +2866,6 @@
                 mTmpApplySurfaceChangesTransactionState.preferredModeId,
                 true /* inTraversal, must call performTraversalInTrans... below */);
 
-        stopDimmingIfNeeded();
-
         final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
         if (wallpaperVisible != mLastWallpaperVisible) {
             mLastWallpaperVisible = wallpaperVisible;
@@ -3062,13 +3069,6 @@
                 // Include this window.
 
                 final WindowStateAnimator winAnim = w.mWinAnimator;
-                int layer = winAnim.mSurfaceController.getLayer();
-                if (mScreenshotApplicationState.maxLayer < layer) {
-                    mScreenshotApplicationState.maxLayer = layer;
-                }
-                if (mScreenshotApplicationState.minLayer > layer) {
-                    mScreenshotApplicationState.minLayer = layer;
-                }
 
                 // Don't include wallpaper in bounds calculation
                 if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
@@ -3112,8 +3112,6 @@
 
             final WindowState appWin = mScreenshotApplicationState.appWin;
             final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
-            final int maxLayer = mScreenshotApplicationState.maxLayer;
-            final int minLayer = mScreenshotApplicationState.minLayer;
 
             if (appToken != null && appWin == null) {
                 // Can't find a window to snapshot.
@@ -3134,11 +3132,6 @@
             // because we don't want to release the mWindowMap lock until the screenshot is
             // taken.
 
-            if (maxLayer == 0) {
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                        + ": returning null maxLayer=" + maxLayer);
-                return null;
-            }
 
             if (!mutableIncludeFullDisplay.value) {
                 // Constrain frame to the screen size.
@@ -3183,8 +3176,6 @@
             convertCropForSurfaceFlinger(crop, rot, dw, dh);
 
             if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                        + maxLayer + " appToken=" + appToken);
                 forAllWindows(w -> {
                     final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
                     Slog.i(TAG_WM, w + ": " + w.mLayer
@@ -3206,11 +3197,13 @@
             SurfaceControl.openTransaction();
             SurfaceControl.closeTransactionSync();
 
-            bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+            // TODO(b/68392460): We should screenshot Task controls directly
+            // but it's difficult at the moment as the Task doesn't have the
+            // correct size set.
+            bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
                     inRotation, rot);
             if (bitmap == null) {
-                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                        + ") to layer " + maxLayer);
+                Slog.w(TAG_WM, "Failed to take screenshot");
                 return null;
             }
         }
@@ -3366,6 +3359,10 @@
      * I.e Activities.
      */
     private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+        /**
+         * A control placed at the appropriate level for transitions to occur.
+         */
+        SurfaceControl mAnimationLayer = null;
 
         // Cached reference to some special stacks we tend to get a lot so we don't need to loop
         // through the list to find them.
@@ -3677,6 +3674,50 @@
             // to prevent freezing/unfreezing the display too early.
             return mLastOrientation;
         }
+
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            final int NORMAL_STACK_STATE = 0;
+            final int BOOSTED_STATE = 1;
+            final int ALWAYS_ON_TOP_STATE = 2;
+
+            // We allow stacks to change visual order from the AM specified order due to
+            // Z-boosting during animations. However we must take care to ensure TaskStacks
+            // which are marked as alwaysOnTop remain that way.
+            int layer = 0;
+            for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+                for (int i = 0; i < mChildren.size(); i++) {
+                    final TaskStack s = mChildren.get(i);
+                    layer++;
+                    if (state == NORMAL_STACK_STATE) {
+                        s.assignLayer(t, layer);
+                    } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+                        s.assignLayer(t, layer);
+                    } else if (state == ALWAYS_ON_TOP_STATE &&
+                            s.isAlwaysOnTop()) {
+                        s.assignLayer(t, layer);
+                    }
+                    s.assignChildLayers(t);
+                }
+                // The appropriate place for App-Transitions to occur is right
+                // above all other animations but still below things in the Picture-and-Picture
+                // windowing mode.
+                if (state == BOOSTED_STATE && mAnimationLayer != null) {
+                    t.setLayer(mAnimationLayer, layer + 1);
+                }
+            }
+        }
+
+        @Override
+        void onParentSet() {
+            super.onParentSet();
+            if (getParent() != null) {
+                mAnimationLayer = makeSurface().build();
+            } else {
+                mAnimationLayer.destroy();
+                mAnimationLayer = null;
+            }
+        }
     }
 
     /**
@@ -3760,4 +3801,119 @@
         E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
                 boolean useIdentityTransform, int rotation);
     }
+
+    SurfaceControl.Builder makeSurface(SurfaceSession s) {
+        return mService.makeSurfaceBuilder(s)
+                .setParent(mWindowingLayer);
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        return mSession;
+    }
+
+    @Override
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        SurfaceSession s = child != null ? child.getSession() : getSession();
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
+        b.setSize(mSurfaceSize, mSurfaceSize);
+
+        if (child == null) {
+            return b;
+        }
+
+        b.setName(child.getName());
+        if (child.isScreenOverlay()) {
+            return b.setParent(mOverlayLayer);
+        } else {
+            return b.setParent(mWindowingLayer);
+        }
+    }
+
+    /**
+     * The makeSurface variants are for use by the window-container
+     * hierarchy. makeOverlay here is a function for various non windowing
+     * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+     * and other potpourii.
+     */
+    SurfaceControl.Builder makeOverlay() {
+        return mService.makeSurfaceBuilder(mSession)
+            .setParent(mOverlayLayer);
+    }
+
+    void applyMagnificationSpec(MagnificationSpec spec) {
+        applyMagnificationSpec(getPendingTransaction(), spec);
+        getPendingTransaction().apply();
+    }
+
+    @Override
+    void onParentSet() {
+        // Since we are the top of the SurfaceControl hierarchy here
+        // we create the root surfaces explicitly rather than chaining
+        // up as the default implementation in onParentSet does. So we
+        // explicitly do NOT call super here.
+    }
+
+    @Override
+    void assignChildLayers(SurfaceControl.Transaction t) {
+        t.setLayer(mOverlayLayer, 1)
+                .setLayer(mWindowingLayer, 0);
+
+        // These are layers as children of "mWindowingLayer"
+        mBelowAppWindowsContainers.assignLayer(t, 0);
+        mTaskStackContainers.assignLayer(t, 1);
+        mAboveAppWindowsContainers.assignLayer(t, 2);
+
+        WindowState imeTarget = mService.mInputMethodTarget;
+        if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+            // In split-screen windowing mode we can't layer the
+            // IME relative to the IME target because it needs to
+            // go over the docked divider, so instead we place it on top
+            // of everything and use relative layering of windows which need
+            // to go above it (see special logic in WindowState#assignLayer)
+            mImeWindowsContainers.assignLayer(t, 3);
+        } else {
+            t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+                    imeTarget.getSurfaceControl(),
+                    // TODO: We need to use an extra level on the app surface to ensure
+                    // this is always above SurfaceView but always below attached window.
+                    1);
+        }
+
+        // Above we have assigned layers to our children, now we ask them to assign
+        // layers to their children.
+        mBelowAppWindowsContainers.assignChildLayers(t);
+        mTaskStackContainers.assignChildLayers(t);
+        mAboveAppWindowsContainers.assignChildLayers(t);
+        mImeWindowsContainers.assignChildLayers(t);
+    }
+
+    /**
+     * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+     * that the IME target is one of the docked applications. We'd like the docked divider to be
+     * above both of the applications, and we'd like the IME to be above the docked divider.
+     * However we need child windows of the applications to be above the IME (Text drag handles).
+     * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+     * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+     * with {@link #WindowState#assignLayer}
+     */
+    void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+        t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+    }
+
+    @Override
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void onPendingTransactionApplied() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index d79ba89..8308417 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -54,7 +54,6 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.LocalServices;
-import com.android.server.wm.DimLayer.DimLayerUser;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
@@ -62,7 +61,7 @@
 /**
  * Keeps information about the docked stack divider.
  */
-public class DockedStackDividerController implements DimLayerUser {
+public class DockedStackDividerController {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
 
@@ -114,7 +113,6 @@
     private boolean mLastVisibility = false;
     private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
             = new RemoteCallbackList<>();
-    private final DimLayer mDimLayer;
 
     private boolean mMinimizedDock;
     private int mOriginalDockedSide = DOCKED_INVALID;
@@ -141,13 +139,12 @@
     private boolean mImeHideRequested;
     private final Rect mLastDimLayerRect = new Rect();
     private float mLastDimLayerAlpha;
+    private TaskStack mDimmedStack;
 
     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
         final Context context = service.mContext;
-        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
-                "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
         loadDimens();
@@ -463,6 +460,11 @@
         }
         mOriginalDockedSide = DOCKED_INVALID;
         setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
+
+        if (mDimmedStack != null) {
+            mDimmedStack.stopDimming();
+            mDimmedStack = null;
+        }
     }
 
     /**
@@ -564,34 +566,12 @@
         final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
-            stack.getDimBounds(mTmpRect);
-            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
-                if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
-                    try {
-                        // TODO: This should use the regular animation transaction - here and below
-                        mService.openSurfaceTransaction();
-                        mDimLayer.setBounds(mTmpRect);
-                        mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
-                    } finally {
-                        mService.closeSurfaceTransaction("setResizeDimLayer");
-                    }
-                }
-                mLastDimLayerRect.set(mTmpRect);
-                mLastDimLayerAlpha = alpha;
-            } else {
-                visibleAndValid = false;
-            }
+            mDimmedStack = stack;
+            stack.dim(alpha);
         }
-        if (!visibleAndValid) {
-            if (mLastDimLayerAlpha != 0f) {
-                try {
-                    mService.openSurfaceTransaction();
-                    mDimLayer.hide();
-                } finally {
-                    mService.closeSurfaceTransaction("setResizeDimLayer");
-                }
-            }
-            mLastDimLayerAlpha = 0f;
+        if (!visibleAndValid && stack != null) {
+            mDimmedStack = null;
+            stack.stopDimming();
         }
     }
 
@@ -829,12 +809,8 @@
             return animateForMinimizedDockedStack(now);
         } else if (mAnimatingForIme) {
             return animateForIme(now);
-        } else {
-            if (mDimLayer != null && mDimLayer.isDimming()) {
-                mDimLayer.setLayer(getResizeDimLayer());
-            }
-            return false;
         }
+        return false;
     }
 
     private boolean animateForIme(long now) {
@@ -942,27 +918,6 @@
                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
     }
 
-    @Override
-    public boolean dimFullscreen() {
-        return false;
-    }
-
-    @Override
-    public DisplayInfo getDisplayInfo() {
-        return mDisplayContent.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mDisplayContent != null;
-    }
-
-    @Override
-    public void getDimBounds(Rect outBounds) {
-        // This dim layer user doesn't need this.
-    }
-
-    @Override
     public String toShortString() {
         return TAG;
     }
@@ -977,10 +932,6 @@
         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
-        if (mDimLayer.isDimming()) {
-            pw.println(prefix + "  Dim layer is dimming: ");
-            mDimLayer.printTo(prefix + "    ", pw);
-        }
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 8bec8d7..fddf6ca 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -49,19 +49,19 @@
     private int mRotation;
     private boolean mVisible;
 
-    public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+    public EmulatorDisplayOverlay(Context context, DisplayContent dc,
             int zOrder) {
+        final Display display = dc.getDisplay();
         mScreenSize = new Point();
         display.getSize(mScreenSize);
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("EmulatorDisplayOverlay")
                     .setSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f541926..43dfccc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,7 +138,6 @@
     ParcelFileDescriptor mSurfaceTraceFd;
     RemoteEventTrace mRemoteEventTrace;
 
-    private final WindowLayersController mLayersController;
     final WallpaperController mWallpaperController;
 
     private final Handler mHandler;
@@ -163,7 +162,6 @@
     RootWindowContainer(WindowManagerService service) {
         mService = service;
         mHandler = new MyHandler(service.mH.getLooper());
-        mLayersController = new WindowLayersController(mService);
         mWallpaperController = new WallpaperController(mService);
     }
 
@@ -231,7 +229,7 @@
     }
 
     private DisplayContent createDisplayContent(final Display display) {
-        final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+        final DisplayContent dc = new DisplayContent(display, mService,
                 mWallpaperController);
         final int displayId = display.getDisplayId();
 
@@ -1103,4 +1101,9 @@
     String getName() {
         return "ROOT";
     }
+
+    @Override
+    void scheduleAnimation() {
+        mService.scheduleAnimationLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3350fea..70bf15c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -225,7 +225,7 @@
     }
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
-            SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+            boolean inTransaction, boolean forceDefaultOrientation,
             boolean isSecure, WindowManagerService service) {
         mService = service;
         mContext = context;
@@ -269,7 +269,7 @@
 
         try {
             try {
-                mSurfaceControl = new SurfaceControl.Builder(session)
+                mSurfaceControl = displayContent.makeOverlay()
                         .setName("ScreenshotSurface")
                         .setSize(mWidth, mHeight)
                         .setSecure(isSecure)
@@ -281,7 +281,6 @@
                 // TODO(multidisplay): we should use the proper display
                 SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
                         SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
-                mSurfaceControl.setLayerStack(display.getLayerStack());
                 mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
                 mSurfaceControl.setAlpha(0);
                 mSurfaceControl.show();
@@ -370,11 +369,11 @@
     }
 
     // Must be called while in a transaction.
-    public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+    public boolean setRotationInTransaction(int rotation,
             long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
         setRotationInTransaction(rotation);
         if (TWO_PHASE_ANIMATION) {
-            return startAnimation(session, maxAnimationDuration, animationScale,
+            return startAnimation(maxAnimationDuration, animationScale,
                     finalWidth, finalHeight, false, 0, 0);
         }
 
@@ -385,7 +384,7 @@
     /**
      * Returns true if animating.
      */
-    private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+    private boolean startAnimation(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, boolean dismissing,
             int exitAnim, int enterAnim) {
         if (mSurfaceControl == null) {
@@ -561,8 +560,8 @@
                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
                         mOriginalWidth*2, mOriginalHeight*2);
                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
-                mCustomBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+                mCustomBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
                 mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -601,8 +600,8 @@
                             mOriginalWidth*2, mOriginalHeight*2);
                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
                 }
-                mExitingBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+                mExitingBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
                 mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -624,8 +623,8 @@
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
                         finalWidth*2, finalHeight*2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
-                mEnteringBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+                mEnteringBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
@@ -642,7 +641,7 @@
     /**
      * Returns true if animating.
      */
-    public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+    public boolean dismiss(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
         if (mSurfaceControl == null) {
@@ -650,7 +649,7 @@
             return false;
         }
         if (!mStarted) {
-            startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+            startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
                     true, exitAnim, enterAnim);
         }
         if (!mStarted) {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index eb8ee69..f51a6a9 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -41,15 +41,14 @@
     private boolean mDrawNeeded;
     private final int mThickness = 20;
 
-    public StrictModeFlash(Display display, SurfaceSession session) {
+    public StrictModeFlash(DisplayContent dc) {
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("StrictModeFlash")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
similarity index 62%
rename from core/java/android/os/IStatsCallbacks.aidl
rename to services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
index 02e7cd3..5390e5a 100644
--- a/core/java/android/os/IStatsCallbacks.aidl
+++ b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.os;
+package com.android.server.wm;
 
-/**
-  * Callback for Statsd to allow binder calls to clients.
-  * {@hide}
-  */
-interface IStatsCallbacks {
-    void onReceiveLogs(out byte[] log);
-}
+import android.view.SurfaceSession;
+import android.view.SurfaceControl;
+
+interface SurfaceBuilderFactory {
+    SurfaceControl.Builder make(SurfaceSession s);
+};
+
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13435d7..f70845e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -51,7 +51,7 @@
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
-class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> {
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
     // Return value from {@link setBounds} indicating no change was made to the Task bounds.
     private static final int BOUNDS_CHANGE_NONE = 0;
@@ -105,6 +105,9 @@
     // stack moves and we in fact do so when moving from full screen to pinned.
     private boolean mPreserveNonFloatingState = false;
 
+    private Dimmer mDimmer = new Dimmer(this);
+    private final Rect mTmpDimBoundsRect = new Rect();
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
             int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
             TaskWindowContainerController controller) {
@@ -188,12 +191,6 @@
         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
         mDeferRemoval = false;
 
-        // Make sure to remove dim layer user first before removing task its from parent.
-        DisplayContent content = getDisplayContent();
-        if (content != null) {
-            content.mDimLayerController.removeDimLayerUser(this);
-        }
-
         super.removeImmediately();
     }
 
@@ -237,6 +234,8 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         // Update task bounds if needed.
         updateDisplayInfo(getDisplayContent());
 
@@ -312,9 +311,7 @@
         mBounds.set(bounds);
 
         mRotation = rotation;
-        if (displayContent != null) {
-            displayContent.mDimLayerController.updateDimLayer(this);
-        }
+
         onOverrideConfigurationChanged(overrideConfig);
         return boundsChange;
     }
@@ -482,7 +479,6 @@
     }
 
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
-    @Override
     public void getDimBounds(Rect out) {
         final DisplayContent displayContent = mStack.getDisplayContent();
         // It doesn't matter if we in particular are part of the resize, since we couldn't have
@@ -634,23 +630,6 @@
         return null;
     }
 
-    @Override
-    public boolean dimFullscreen() {
-        return isFullscreen();
-    }
-
-    @Override
-    public int getLayerForDim(WindowStateAnimator animator, int layerOffset, int defaultLayer) {
-        // If the dim layer is for a starting window, move the dim layer back in the z-order behind
-        // the lowest activity window to ensure it does not occlude the main window if it is
-        // translucent
-        final AppWindowToken appToken = animator.mWin.mAppToken;
-        if (animator.mAttrType == TYPE_APPLICATION_STARTING && hasChild(appToken) ) {
-            return Math.min(defaultLayer, appToken.getLowestAnimLayer() - layerOffset);
-        }
-        return defaultLayer;
-    }
-
     boolean isFullscreen() {
         if (useCurrentBounds()) {
             return mFillsParent;
@@ -661,16 +640,6 @@
         return true;
     }
 
-    @Override
-    public DisplayInfo getDisplayInfo() {
-        return getDisplayContent().getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return getDisplayContent() != null;
-    }
-
     void forceWindowsScaleable(boolean force) {
         mService.openSurfaceTransaction();
         try {
@@ -718,9 +687,18 @@
         mPreserveNonFloatingState = false;
     }
 
+    Dimmer getDimmer() {
+        return mDimmer;
+    }
+
     @Override
-    public String toShortString() {
-        return "Task=" + mTaskId;
+    void prepareSurfaces() {
+        mDimmer.resetDimStates();
+        super.prepareSurfaces();
+        getDimBounds(mTmpDimBoundsRect);
+        if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+            scheduleAnimation();
+        }
     }
 
     @CallSuper
@@ -757,4 +735,8 @@
             wtoken.dump(pw, triplePrefix);
         }
     }
+
+    String toShortString() {
+        return "Task=" + mTaskId;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 12f6b5a..5d4ba09 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -59,7 +59,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-class TaskPositioner implements DimLayer.DimLayerUser {
+class TaskPositioner {
     private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
     private static final String TAG_LOCAL = "TaskPositioner";
     private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
@@ -99,9 +99,6 @@
     private WindowPositionerEventReceiver mInputEventReceiver;
     private Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
-    private DimLayer mDimLayer;
-    @CtrlType
-    private int mCurrentDimSide;
     private Rect mTmpRect = new Rect();
     private int mSideMargin;
     private int mMinVisibleWidth;
@@ -207,15 +204,6 @@
                             mService.mActivityManager.resizeTask(
                                     mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
                         }
-
-                        if (mCurrentDimSide != CTRL_NONE) {
-                            final int createMode = mCurrentDimSide == CTRL_LEFT
-                                    ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                                    : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-                            mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
-                                    mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
-                                    null /* initialBounds */);
-                        }
                     } catch(RemoteException e) {}
 
                     // Post back to WM to handle clean-ups. We still need the input
@@ -243,7 +231,9 @@
     /**
      * @param display The Display that the window being dragged is on.
      */
-    void register(Display display) {
+    void register(DisplayContent displayContent) {
+        final Display display = displayContent.getDisplay();
+
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "Registering task positioner");
         }
@@ -305,7 +295,6 @@
         }
         mService.pauseRotationLocked();
 
-        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -336,12 +325,6 @@
         mDragWindowHandle = null;
         mDragApplicationHandle = null;
         mDisplay = null;
-
-        if (mDimLayer != null) {
-            mDimLayer.destroySurface();
-            mDimLayer = null;
-        }
-        mCurrentDimSide = CTRL_NONE;
         mDragEnded = true;
 
         // Resume rotations after a drag.
@@ -434,7 +417,6 @@
         }
 
         updateWindowDragBounds(nX, nY, mTmpRect);
-        updateDimLayerVisibility(nX);
         return false;
     }
 
@@ -621,88 +603,6 @@
                 "updateWindowDragBounds: " + mWindowDragBounds);
     }
 
-    private void updateDimLayerVisibility(int x) {
-        @CtrlType
-        int dimSide = getDimSide(x);
-        if (dimSide == mCurrentDimSide) {
-            return;
-        }
-
-        mCurrentDimSide = dimSide;
-
-        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
-        mService.openSurfaceTransaction();
-        if (mCurrentDimSide == CTRL_NONE) {
-            mDimLayer.hide();
-        } else {
-            showDimLayer();
-        }
-        mService.closeSurfaceTransaction("updateDimLayerVisibility");
-    }
-
-    /**
-     * Returns the side of the screen the dim layer should be shown.
-     * @param x horizontal coordinate used to determine if the dim layer should be shown
-     * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
-     * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
-     * shouldn't be shown.
-     */
-    private int getDimSide(int x) {
-        if (!mTask.mStack.inFreeformWindowingMode()
-                || !mTask.mStack.fillsParent()
-                || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
-            return CTRL_NONE;
-        }
-
-        mTask.mStack.getDimBounds(mTmpRect);
-        if (x - mSideMargin <= mTmpRect.left) {
-            return CTRL_LEFT;
-        }
-        if (x + mSideMargin >= mTmpRect.right) {
-            return CTRL_RIGHT;
-        }
-
-        return CTRL_NONE;
-    }
-
-    private void showDimLayer() {
-        mTask.mStack.getDimBounds(mTmpRect);
-        if (mCurrentDimSide == CTRL_LEFT) {
-            mTmpRect.right = mTmpRect.centerX();
-        } else if (mCurrentDimSide == CTRL_RIGHT) {
-            mTmpRect.left = mTmpRect.centerX();
-        }
-
-        mDimLayer.setBounds(mTmpRect);
-        mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
-                RESIZING_HINT_DURATION_MS);
-    }
-
-    @Override /** {@link DimLayer.DimLayerUser} */
-    public boolean dimFullscreen() {
-        return isFullscreen();
-    }
-
-    boolean isFullscreen() {
-        return false;
-    }
-
-    @Override /** {@link DimLayer.DimLayerUser} */
-    public DisplayInfo getDisplayInfo() {
-        return mTask.mStack.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mTask != null && mTask.getDisplayContent() != null;
-    }
-
-    @Override
-    public void getDimBounds(Rect out) {
-        // This dim layer user doesn't need this.
-    }
-
-    @Override
     public String toShortString() {
         return TAG;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 053fb47..f9062a8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -63,7 +64,7 @@
 
 import java.io.PrintWriter;
 
-public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements
         BoundsAnimationTarget {
     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
      * restrict IME adjustment so that a min portion of top stack remains visible.*/
@@ -108,8 +109,8 @@
     /** Density as of last time {@link #mBounds} was set. */
     private int mDensity;
 
-    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
-    private DimLayer mAnimationBackgroundSurface;
+    private SurfaceControl mAnimationBackgroundSurface;
+    private boolean mAnimationBackgroundSurfaceIsShown = false;
 
     /** The particular window with an Animation with non-zero background color. */
     private WindowStateAnimator mAnimationBackgroundAnimator;
@@ -149,6 +150,13 @@
 
     Rect mPreAnimationBounds = new Rect();
 
+    private Dimmer mDimmer = new Dimmer(this);
+
+    /**
+     * For {@link #prepareSurfaces}.
+     */
+    final Rect mTmpDimBoundsRect = new Rect();
+
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         mService = service;
         mStackId = stackId;
@@ -245,6 +253,35 @@
         }
     }
 
+    private void setAnimationBackgroundBounds(Rect bounds) {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
+                .setPosition(mAnimationBackgroundSurface, 0, 0);
+        scheduleAnimation();
+    }
+
+    private void hideAnimationSurface() {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().hide(mAnimationBackgroundSurface);
+        mAnimationBackgroundSurfaceIsShown = false;
+        scheduleAnimation();
+    }
+
+    private void showAnimationSurface(float alpha) {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
+                .setAlpha(mAnimationBackgroundSurface, alpha)
+                .show(mAnimationBackgroundSurface);
+        mAnimationBackgroundSurfaceIsShown = true;
+        scheduleAnimation();
+    }
+
     private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFillsParent;
         int rotation = Surface.ROTATION_0;
@@ -267,10 +304,7 @@
             return false;
         }
 
-        if (mDisplayContent != null) {
-            mDisplayContent.mDimLayerController.updateDimLayer(this);
-            mAnimationBackgroundSurface.setBounds(bounds);
-        }
+        setAnimationBackgroundBounds(bounds);
 
         mBounds.set(bounds);
         mRotation = rotation;
@@ -368,7 +402,6 @@
     }
 
     /** Bounds of the stack with other system factors taken into consideration. */
-    @Override
     public void getDimBounds(Rect out) {
         getBounds(out);
     }
@@ -700,9 +733,12 @@
         }
 
         mDisplayContent = dc;
-        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
-                "animation background stackId=" + mStackId);
+
         updateBoundsForWindowModeChange();
+        mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+            .setName("animation background stackId=" + mStackId)
+            .build();
+
         super.onDisplayChanged(dc);
     }
 
@@ -914,16 +950,16 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         if (getParent() != null || mDisplayContent == null) {
             return;
         }
 
-        // Looks like the stack was removed from the display. Go ahead and clean things up.
-        mDisplayContent.mDimLayerController.removeDimLayerUser(this);
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
         if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.destroySurface();
+            mAnimationBackgroundSurface.destroy();
             mAnimationBackgroundSurface = null;
         }
 
@@ -933,9 +969,7 @@
 
     void resetAnimationBackgroundAnimator() {
         mAnimationBackgroundAnimator = null;
-        if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.hide();
-        }
+        hideAnimationSurface();
     }
 
     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -944,8 +978,7 @@
                 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
             mAnimationBackgroundAnimator = winAnimator;
             animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
-            mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
-                    ((color >> 24) & 0xff) / 255f, 0);
+            showAnimationSurface(((color >> 24) & 0xff) / 255f);
         }
     }
 
@@ -1250,7 +1283,7 @@
         }
         proto.write(FILLS_PARENT, mFillsParent);
         mBounds.writeToProto(proto, BOUNDS);
-        proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+        proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
         proto.end(token);
     }
 
@@ -1273,9 +1306,8 @@
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
             mChildren.get(taskNdx).dump(prefix + "  ", pw);
         }
-        if (mAnimationBackgroundSurface.isDimming()) {
-            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
-            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
+        if (mAnimationBackgroundSurfaceIsShown) {
+            pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
         }
         if (!mExitingAppTokens.isEmpty()) {
             pw.println();
@@ -1299,11 +1331,6 @@
     }
 
     @Override
-    public boolean dimFullscreen() {
-        return !isActivityTypeStandard() || fillsParent();
-    }
-
-    @Override
     boolean fillsParent() {
         if (useCurrentBounds()) {
             return mFillsParent;
@@ -1315,16 +1342,6 @@
     }
 
     @Override
-    public DisplayInfo getDisplayInfo() {
-        return mDisplayContent.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mDisplayContent != null;
-    }
-
-    @Override
     public String toString() {
         return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
     }
@@ -1333,7 +1350,6 @@
         return toShortString();
     }
 
-    @Override
     public String toShortString() {
         return "Stack=" + mStackId;
     }
@@ -1691,4 +1707,32 @@
                 || activityType == ACTIVITY_TYPE_RECENTS
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
+
+    Dimmer getDimmer() {
+        return mDimmer;
+    }
+
+    @Override
+    void prepareSurfaces() {
+        mDimmer.resetDimStates();
+        super.prepareSurfaces();
+        getDimBounds(mTmpDimBoundsRect);
+        if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+            scheduleAnimation();
+        }
+    }
+
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    void dim(float alpha) {
+        mDimmer.dimAbove(getPendingTransaction(), alpha);
+        scheduleAnimation();
+    }
+
+    void stopDimming() {
+        mDimmer.stopDim(getPendingTransaction());
+        scheduleAnimation();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index d97aaac..9216b66 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -53,7 +53,7 @@
     private int mLastDH;
     private boolean mDrawNeeded;
 
-    Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+    Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
         if (false) {
             Log.i(TAG_WM, "*********************** WATERMARK");
             for (int i=0; i<tokens.length; i++) {
@@ -61,7 +61,7 @@
             }
         }
 
-        mDisplay = display;
+        mDisplay = dc.getDisplay();
         mTokens = tokens;
 
         StringBuilder builder = new StringBuilder(32);
@@ -114,7 +114,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("WatermarkSurface")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 1912095..20bade67 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -200,7 +200,7 @@
                     ++mAnimTransactionSequence;
                     dc.updateWindowsForAnimator(this);
                     dc.updateWallpaperForAnimator(this);
-                    dc.prepareWindowSurfaces();
+                    dc.prepareSurfaces();
                 }
 
                 for (int i = 0; i < numDisplays; i++) {
@@ -214,8 +214,6 @@
                     if (screenRotationAnimation != null) {
                         screenRotationAnimation.updateSurfacesInTransaction();
                     }
-
-                    orAnimating(dc.animateDimLayers());
                     orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
                     //TODO (multidisplay): Magnification is supported only for the default display.
                     if (accessibilityController != null && dc.isDefaultDisplay) {
@@ -237,6 +235,13 @@
                 if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
             }
 
+            final int numDisplays = mDisplayContentsAnimators.size();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+                dc.onPendingTransactionApplied();
+            }
+
             boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
             boolean doRequest = false;
             if (mBulkUpdateParams != 0) {
@@ -271,6 +276,7 @@
             mService.destroyPreservedSurfaceLocked();
             mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
+
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                         + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f4b897..a5e6288 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.util.Pools;
 
 import android.util.proto.ProtoOutputStream;
@@ -64,7 +68,14 @@
             new Pools.SynchronizedPool<>(3);
 
     // The owner/creator for this container. No controller if null.
-    private WindowContainerController mController;
+     WindowContainerController mController;
+
+    protected SurfaceControl mSurfaceControl;
+
+    /**
+     * Applied as part of the animation pass in "prepareSurfaces".
+     */
+    private Transaction mPendingTransaction = new Transaction();
 
     @Override
     final protected WindowContainer getParent() {
@@ -101,7 +112,22 @@
      * Supposed to be overridden and contain actions that should be executed after parent was set.
      */
     void onParentSet() {
-        // Do nothing by default.
+        if (mParent == null) {
+            return;
+        }
+        if (mSurfaceControl == null) {
+            // If we don't yet have a surface, but we now have a parent, we should
+            // build a surface.
+            mSurfaceControl = makeSurface().build();
+            getPendingTransaction().show(mSurfaceControl);
+        } else {
+            // If we have a surface but a new parent, we just need to perform a reparent.
+            getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+        }
+
+        // Either way we need to ask the parent to assign us a Z-order.
+        mParent.assignChildLayers();
+        scheduleAnimation();
     }
 
     // Temp. holders for a chain of containers we are currently processing.
@@ -188,6 +214,11 @@
             mChildren.remove(child);
         }
 
+        if (mSurfaceControl != null) {
+            destroyAfterPendingTransaction(mSurfaceControl);
+            mSurfaceControl = null;
+        }
+
         if (mParent != null) {
             mParent.removeChild(this);
         }
@@ -195,6 +226,7 @@
         if (mController != null) {
             setController(null);
         }
+
     }
 
     /**
@@ -407,7 +439,7 @@
     }
 
     /**
-a     * Returns whether this child is on top of the window hierarchy.
+     * @return Whether this child is on top of the window hierarchy.
      */
     boolean isOnTop() {
         return getParent().getTopChild() == this && getParent().isOnTop();
@@ -673,6 +705,103 @@
         mController = controller;
     }
 
+    SurfaceControl.Builder makeSurface() {
+        final WindowContainer p = getParent();
+        return p.makeChildSurface(this);
+    }
+
+    /**
+     * @param child The WindowContainer this child surface is for, or null if the Surface
+     *              is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
+     */
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        final WindowContainer p = getParent();
+        // Give the parent a chance to set properties. In hierarchy v1 we rely
+        // on this to set full-screen dimensions on all our Surface-less Layers.
+        final SurfaceControl.Builder b = p.makeChildSurface(child);
+        if (child != null && child.isScreenOverlay()) {
+            // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+            // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+            // and we shouldn't set ourselves as the parent.
+            return b;
+        } else {
+            return b.setParent(mSurfaceControl);
+        }
+    }
+
+    /**
+     * There are various layers which require promotion from the WindowContainer
+     * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+     * for the particular usage.
+     *
+     * TODO: Perhaps this should be eliminated, either through modifying
+     * the window container hierarchy or through modifying the way we express these overlay
+     * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+     * Flash and not actually use a WindowState).
+     */
+    boolean isScreenOverlay() {
+        return false;
+    }
+
+    /**
+     * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+     */
+    boolean shouldMagnify() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (!mChildren.get(i).shouldMagnify()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    SurfaceSession getSession() {
+        if (getParent() != null) {
+            return getParent().getSession();
+        }
+        return null;
+    }
+
+    void assignLayer(Transaction t, int layer) {
+        if (mSurfaceControl != null) {
+            t.setLayer(mSurfaceControl, layer);
+        }
+    }
+
+    void assignChildLayers(Transaction t) {
+        int layer = 0;
+        boolean boosting = false;
+
+        // We use two passes as a way to promote children which
+        // need Z-boosting to the end of the list.
+        for (int i = 0; i < 2; i++ ) {
+            for (int j = 0; j < mChildren.size(); ++j) {
+                final WindowContainer wc = mChildren.get(j);
+                if (wc.needsZBoost() && !boosting) {
+                    continue;
+                }
+                wc.assignLayer(t, layer);
+                wc.assignChildLayers(t);
+
+                layer++;
+            }
+            boosting = true;
+        }
+    }
+
+    void assignChildLayers() {
+        assignChildLayers(getPendingTransaction());
+    }
+
+    boolean needsZBoost() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (mChildren.get(i).needsZBoost()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
      * {@link com.android.server.wm.proto.WindowContainerProto}.
@@ -719,4 +848,59 @@
             mConsumerWrapperPool.release(this);
         }
     }
+
+    // TODO(b/68336570): Should this really be on WindowContainer since it
+    // can only be used on the top-level nodes that aren't animated?
+    // (otherwise we would be fighting other callers of setMatrix).
+    void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+        if (shouldMagnify()) {
+            t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+                    .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+        } else {
+            for (int i = 0; i < mChildren.size(); i++) {
+                mChildren.get(i).applyMagnificationSpec(t, spec);
+            }
+        }
+    }
+
+    /**
+     * TODO: Once we totally eliminate global transaction we will pass transaction in here
+     * rather than merging to global.
+     */
+    void prepareSurfaces() {
+        SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+        for (int i = 0; i < mChildren.size(); i++) {
+            mChildren.get(i).prepareSurfaces();
+        }
+    }
+
+    /**
+     * Trigger a call to prepareSurfaces from the animation thread, such that
+     * mPendingTransaction will be applied.
+     */
+    void scheduleAnimation() {
+        if (mParent != null) {
+            mParent.scheduleAnimation();
+        }
+    }
+
+    SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
+    /**
+     * Destroy a given surface after executing mPendingTransaction. This is
+     * largely a workaround for destroy not being part of transactions
+     * rather than an intentional design, so please take care when
+     * expanding use.
+     */
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        if (mParent != null) {
+            mParent.destroyAfterPendingTransaction(surface);
+        }
+    }
+    
+    Transaction getPendingTransaction() {
+        return mPendingTransaction;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
deleted file mode 100644
index 7caf2fe..0000000
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2015 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.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
-    private final WindowManagerService mService;
-
-    WindowLayersController(WindowManagerService service) {
-        mService = service;
-    }
-
-    private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
-    private WindowState mDockDivider = null;
-    private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
-    private int mCurBaseLayer;
-    private int mCurLayer;
-    private boolean mAnyLayerChanged;
-    private int mHighestApplicationLayer;
-    private int mHighestDockedAffectedLayer;
-    private int mHighestLayerInImeTargetBaseLayer;
-    private WindowState mImeTarget;
-    private boolean mAboveImeTarget;
-    private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
-    private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
-        boolean layerChanged = false;
-
-        int oldLayer = w.mLayer;
-        if (w.mBaseLayer == mCurBaseLayer) {
-            mCurLayer += WINDOW_LAYER_MULTIPLIER;
-        } else {
-            mCurBaseLayer = mCurLayer = w.mBaseLayer;
-        }
-        assignAnimLayer(w, mCurLayer);
-
-        // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
-        // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
-        // layer reassignment.
-        if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
-            layerChanged = true;
-            mAnyLayerChanged = true;
-        }
-
-        if (w.mAppToken != null) {
-            mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
-            mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
-            mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-
-        collectSpecialWindows(w);
-
-        if (layerChanged) {
-            w.scheduleAnimationIfDimming();
-        }
-    };
-
-    final void assignWindowLayers(DisplayContent dc) {
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
-                new RuntimeException("here").fillInStackTrace());
-
-        reset();
-        dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
-        adjustSpecialWindows();
-
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mAnyLayerChanged
-                && dc.getDisplayId() == DEFAULT_DISPLAY) {
-            mService.mAccessibilityController.onWindowLayersChangedLocked();
-        }
-
-        if (DEBUG_LAYERS) logDebugLayers(dc);
-    }
-
-    private void logDebugLayers(DisplayContent dc) {
-        dc.forAllWindows((w) -> {
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
-                    + " mLayer=" + w.mLayer + (w.mAppToken == null
-                    ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
-                    + " =mAnimLayer=" + winAnimator.mAnimLayer);
-        }, false /* traverseTopToBottom */);
-    }
-
-    private void reset() {
-        mPinnedWindows.clear();
-        mInputMethodWindows.clear();
-        mDockedWindows.clear();
-        mAssistantWindows.clear();
-        mReplacingWindows.clear();
-        mDockDivider = null;
-
-        mCurBaseLayer = 0;
-        mCurLayer = 0;
-        mAnyLayerChanged = false;
-
-        mHighestApplicationLayer = 0;
-        mHighestDockedAffectedLayer = 0;
-        mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
-        mImeTarget = mService.mInputMethodTarget;
-        mAboveImeTarget = false;
-        mAboveImeTargetAppWindows.clear();
-    }
-
-    private void collectSpecialWindows(WindowState w) {
-        if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
-            mDockDivider = w;
-            return;
-        }
-        if (w.mWillReplaceWindow) {
-            mReplacingWindows.add(w);
-        }
-        if (w.mIsImWindow) {
-            mInputMethodWindows.add(w);
-            return;
-        }
-        if (mImeTarget != null) {
-            if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
-                // Child windows of the ime target with a positive sub-layer should be placed above
-                // the IME.
-                mAboveImeTargetAppWindows.add(w);
-            } else if (mAboveImeTarget && w.mAppToken != null) {
-                // windows of apps above the IME target should be placed above the IME.
-                mAboveImeTargetAppWindows.add(w);
-            }
-            if (w == mImeTarget) {
-                mAboveImeTarget = true;
-            }
-        }
-
-        final int windowingMode = w.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            mPinnedWindows.add(w);
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mDockedWindows.add(w);
-        }
-        if (w.isActivityTypeAssistant()) {
-            mAssistantWindows.add(w);
-        }
-    }
-
-    private void adjustSpecialWindows() {
-        // The following adjustments are beyond the highest docked-affected layer
-        int layer = mHighestDockedAffectedLayer +  TYPE_LAYER_OFFSET;
-
-        // Adjust the docked stack windows and dock divider above only the windows that are affected
-        // by the docked stack. When this happens, also boost the assistant window layers, otherwise
-        // the docked stack windows & divider would be promoted above the assistant.
-        if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
-            while (!mDockedWindows.isEmpty()) {
-                final WindowState window = mDockedWindows.remove();
-                layer = assignAndIncreaseLayerIfNeeded(window, layer);
-            }
-
-            layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
-            while (!mAssistantWindows.isEmpty()) {
-                final WindowState window = mAssistantWindows.remove();
-                if (window.mLayer > mHighestDockedAffectedLayer) {
-                    layer = assignAndIncreaseLayerIfNeeded(window, layer);
-                }
-            }
-        }
-
-        // The following adjustments are beyond the highest app layer or boosted layer
-        layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
-        // We know that we will be animating a relaunching window in the near future, which will
-        // receive a z-order increase. We want the replaced window to immediately receive the same
-        // treatment, e.g. to be above the dock divider.
-        while (!mReplacingWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
-        }
-
-        while (!mPinnedWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
-        }
-
-        // Make sure IME is the highest window in the base layer of it's target.
-        if (mImeTarget != null) {
-            if (mImeTarget.mAppToken == null) {
-                // For non-app ime targets adjust the layer we start from to match what we found
-                // when assigning layers. Otherwise, just use the highest app layer we have some far.
-                layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
-            }
-
-            while (!mInputMethodWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
-            }
-
-            // Adjust app windows the should be displayed above the IME since they are above the IME
-            // target.
-            while (!mAboveImeTargetAppWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
-            }
-        }
-
-    }
-
-    private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
-        if (win != null) {
-            assignAnimLayer(win, layer);
-            // Make sure we leave space in-between normal windows for dims and such.
-            layer += WINDOW_LAYER_MULTIPLIER;
-        }
-        return layer;
-    }
-
-    private void assignAnimLayer(WindowState w, int layer) {
-        w.mLayer = layer;
-        w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
-                + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null) {
-            w.mAppToken.mAppAnimator.updateThumbnailLayer();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ce34306..4656539 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -529,7 +529,6 @@
 
     AccessibilityController mAccessibilityController;
 
-    final SurfaceSession mFxSession;
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
     CircularDisplayMask mCircularDisplayMask;
@@ -804,6 +803,13 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
+    class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new SurfaceControl.Builder(s);
+        }
+    };
+    SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
     static void boostPriorityForLockedSection() {
         sThreadPriorityBooster.boost();
     }
@@ -992,7 +998,6 @@
             mPointerEventDispatcher = null;
         }
 
-        mFxSession = new SurfaceSession();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mDisplays = mDisplayManager.getDisplays();
         for (Display display : mDisplays) {
@@ -2478,11 +2483,8 @@
                 mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
-                if (displayContent.isDimming()) {
-                    anim[0] = anim[1] = 0;
-                } else {
-                    mPolicy.selectRotationAnimationLw(anim);
-                }
+                mPolicy.selectRotationAnimationLw(anim);
+
                 startFreezingDisplayLocked(false, anim[0], anim[1], displayContent);
                 config = new Configuration(mTempConfiguration);
             }
@@ -3592,8 +3594,7 @@
                                 com.android.internal.R.dimen.circular_display_mask_thickness);
 
                         mCircularDisplayMask = new CircularDisplayMask(
-                                getDefaultDisplayContentLocked().getDisplay(),
-                                mFxSession,
+                                getDefaultDisplayContentLocked(),
                                 mPolicy.getWindowLayerFromTypeLw(
                                         WindowManager.LayoutParams.TYPE_POINTER)
                                         * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
@@ -3621,8 +3622,7 @@
                 if (mEmulatorDisplayOverlay == null) {
                     mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
                             mContext,
-                            getDefaultDisplayContentLocked().getDisplay(),
-                            mFxSession,
+                            getDefaultDisplayContentLocked(),
                             mPolicy.getWindowLayerFromTypeLw(
                                     WindowManager.LayoutParams.TYPE_POINTER)
                                     * TYPE_LAYER_MULTIPLIER + 10);
@@ -3670,7 +3670,7 @@
                 // TODO(multi-display): support multiple displays
                 if (mStrictModeFlash == null) {
                     mStrictModeFlash = new StrictModeFlash(
-                            getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+                            getDefaultDisplayContentLocked());
                 }
                 mStrictModeFlash.setVisibility(on);
             } finally {
@@ -4522,7 +4522,7 @@
 
         Display display = displayContent.getDisplay();
         mTaskPositioner = new TaskPositioner(this);
-        mTaskPositioner.register(display);
+        mTaskPositioner.register(displayContent);
         mInputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We need to grab the touch focus so that the touch events during the
@@ -5892,7 +5892,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+                    inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -5952,11 +5952,10 @@
             // TODO(multidisplay): rotation on main screen only.
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
             // Get rotation animation again, with new top window
-            boolean isDimming = displayContent.isDimming();
-            if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
+            if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
                 mExitAnimId = mEnterAnimId = 0;
             }
-            if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+            if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
                     getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                         displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                 scheduleAnimationLocked();
@@ -6040,8 +6039,8 @@
                 if (toks != null && toks.length > 0) {
                     // TODO(multi-display): Show watermarks on secondary displays.
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                    mWatermark = new Watermark(displayContent.getDisplay(),
-                            displayContent.mRealDisplayMetrics, mFxSession, toks);
+                    mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+                            toks);
                 }
             }
         } catch (FileNotFoundException e) {
@@ -7557,4 +7556,13 @@
             w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
         }, false /* traverseTopToBottom */);
     }
+
+    public void applyMagnificationSpec(MagnificationSpec spec) {
+        getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+    }
+
+    SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+        return mSurfaceBuilderFactory.make(s);
+    }
 }
+
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6b1932d..52b7a25 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -41,6 +42,7 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -54,6 +56,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -147,6 +152,8 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
@@ -602,6 +609,14 @@
                 };
             };
 
+    /**
+     * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
+     * container.
+     */
+    private boolean mIsDimming = false;
+
+    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
@@ -2034,23 +2049,6 @@
         return isVisibleOrAdding();
     }
 
-    void scheduleAnimationIfDimming() {
-        final DisplayContent dc = getDisplayContent();
-        if (dc == null) {
-            return;
-        }
-
-        // If layout is currently deferred, we want to hold of with updating the layers.
-        if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
-            return;
-        }
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
-            // Force an animation pass just to update the mDimLayer layer.
-            mService.scheduleAnimationLocked();
-        }
-    }
-
     private final class DeadWindowEventReceiver extends InputEventReceiver {
         DeadWindowEventReceiver(InputChannel inputChannel) {
             super(inputChannel, mService.mH.getLooper());
@@ -2106,31 +2104,12 @@
         mInputWindowHandle.inputChannel = null;
     }
 
-    void applyDimLayerIfNeeded() {
-        // When the app is terminated (eg. from Recents), the task might have already been
-        // removed with the window pending removal. Don't apply dim in such cases, as there
-        // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
-        final AppWindowToken token = mAppToken;
-        if (token != null && token.removed) {
-            return;
-        }
-
-        final DisplayContent dc = getDisplayContent();
-        if (!mAnimatingExit && mAppDied) {
-            // If app died visible, apply a dim over the window to indicate that it's inactive
-            dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
-        } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && dc != null && !mAnimatingExit && isVisible()) {
-            dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
-        }
-    }
-
-    private DimLayer.DimLayerUser getDimLayerUser() {
+    private Dimmer getDimmer() {
         Task task = getTask();
         if (task != null) {
-            return task;
+            return task.getDimmer();
         }
-        return getStack();
+        return getStack().getDimmer();
     }
 
     /** Returns true if the replacement window was removed. */
@@ -2152,9 +2131,6 @@
 
     private void removeReplacedWindow() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
-        if (isDimming()) {
-            transferDimToReplacement();
-        }
         mWillReplaceWindow = false;
         mAnimateReplacingWindow = false;
         mReplacingRemoveRequested = false;
@@ -2217,11 +2193,11 @@
             // need to intercept touches outside of that window. The dim layer user
             // associated with the window (task or stack) will give us the good bounds, as
             // they would be used to display the dim layer.
-            final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-            if (dimLayerUser != null) {
-                dimLayerUser.getDimBounds(mTmpRect);
+            final Task task = getTask();
+            if (task != null) {
+                task.getDimBounds(mTmpRect);
             } else {
-                getVisibleBounds(mTmpRect);
+                getStack().getDimBounds(mTmpRect);
             }
             if (inFreeformWindowingMode()) {
                 // For freeform windows we the touch region to include the whole surface for the
@@ -2729,14 +2705,6 @@
         return displayContent.isDefaultDisplay;
     }
 
-    @Override
-    public boolean isDimming() {
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        final DisplayContent dc = getDisplayContent();
-        return dimLayerUser != null && dc != null
-                && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
-    }
-
     void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
         mShowToOwnerOnly = showToOwnerOnly;
     }
@@ -3071,7 +3039,7 @@
         if (task == null) {
             return false;
         }
-        if (!inSplitScreenWindowingMode()) {
+        if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
             return false;
         }
         if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
@@ -3591,15 +3559,6 @@
         return winY;
     }
 
-    private void transferDimToReplacement() {
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        final DisplayContent dc = getDisplayContent();
-        if (dimLayerUser != null && dc != null) {
-            dc.mDimLayerController.applyDim(dimLayerUser,
-                    mReplacementWindow.mWinAnimator, (mAttrs.flags & FLAG_DIM_BEHIND) != 0);
-        }
-    }
-
     // During activity relaunch due to resize, we sometimes use window replacement
     // for only child windows (as the main window is handled by window preservation)
     // and the big surface.
@@ -4388,4 +4347,80 @@
             return false;
         }
     }
+
+    @Override
+    boolean shouldMagnify() {
+        if (mAttrs.type == TYPE_INPUT_METHOD ||
+                mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+            return false;
+        } else if (isScreenOverlay()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    boolean isScreenOverlay() {
+        // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+        // worry not: it's a fake TYPE_NAVIGATION_BAR.
+        if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+                mAttrs.type == TYPE_NAVIGATION_BAR ||
+                mAttrs.type == TYPE_STATUS_BAR) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        if (mSession.mSurfaceSession != null) {
+            return mSession.mSurfaceSession;
+        } else {
+            return getParent().getSession();
+        }
+    }
+
+    @Override
+    boolean needsZBoost() {
+        return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+    }
+
+    @Override
+    SurfaceControl.Builder makeSurface() {
+        return mToken.makeChildSurface(this);
+    }
+
+
+    @Override
+    void prepareSurfaces() {
+        mIsDimming = false;
+        if (!mAnimatingExit && mAppDied) {
+            mIsDimming = true;
+            getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+        } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+                && !mAnimatingExit && isVisible()) {
+            mIsDimming = true;
+            getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+        }
+
+        mWinAnimator.prepareSurfaceLocked(true);
+        super.prepareSurfaces();
+    }
+
+    @Override
+    void assignLayer(Transaction t, int layer) {
+        // See comment in assignRelativeLayerForImeTargetChild
+        if (!isChildWindow()
+                || (mService.mInputMethodTarget != getParentWindow())
+                || !inSplitScreenWindowingMode()) {
+            super.assignLayer(t, layer);
+            return;
+        }
+        getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+    }
+
+    @Override
+    public boolean isDimming() {
+        return mIsDimming;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86397ae..840cc40 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -559,7 +558,10 @@
         }
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
         if (mSurfaceController != null) {
-            mSurfaceController.setLayer(mAnimLayer + 1);
+            // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+            // window-state. We want this old Surface to stay on top of the new one
+            // until we do the swap, so we place it at layer 1.
+            mSurfaceController.mSurfaceControl.setLayer(1);
         }
         mDestroyPreservedSurfaceUponRedraw = true;
         mSurfaceDestroyDeferred = true;
@@ -730,7 +732,6 @@
         try {
             mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
             mSurfaceController.setLayerStackInTransaction(getLayerStack());
-            mSurfaceController.setLayer(mAnimLayer);
         } finally {
             mService.closeSurfaceTransaction("createSurfaceLocked");
         }
@@ -867,22 +868,6 @@
         mPendingDestroySurface = null;
     }
 
-    void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
-        final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
-        final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
-        if (spec != null && !spec.isNop()) {
-            float scale = spec.scale;
-            transform.postScale(scale, scale);
-            transform.postTranslate(spec.offsetX, spec.offsetY);
-
-            // As we are scaling the whole surface, to keep the content
-            // in the same position we will also have to scale the surfaceInsets.
-            transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
-                    -(surfaceInsetTop*scale - surfaceInsetTop));
-        }
-    }
-
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -969,11 +954,6 @@
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
 
-            MagnificationSpec spec = getMagnificationSpec();
-            if (spec != null) {
-                applyMagnificationSpec(spec, tmpMatrix);
-            }
-
             // "convert" it into SurfaceFlinger's format
             // (a 2x2 matrix + an offset)
             // Here we must not transform the position of the surface
@@ -1057,49 +1037,16 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null) {
-            final Rect frame = mWin.mFrame;
-            final float tmpFloats[] = mService.mTmpFloats;
-            final Matrix tmpMatrix = mWin.mTmpMatrix;
-
-            tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
-            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
-            applyMagnificationSpec(spec, tmpMatrix);
-
-            tmpMatrix.getValues(tmpFloats);
-
-            mHaveMatrix = true;
-            mDsDx = tmpFloats[Matrix.MSCALE_X];
-            mDtDx = tmpFloats[Matrix.MSKEW_Y];
-            mDtDy = tmpFloats[Matrix.MSKEW_X];
-            mDsDy = tmpFloats[Matrix.MSCALE_Y];
-            float x = tmpFloats[Matrix.MTRANS_X];
-            float y = tmpFloats[Matrix.MTRANS_Y];
-            mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
-            mShownAlpha = mAlpha;
-        } else {
-            mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
-            if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-                mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
-            }
-            mShownAlpha = mAlpha;
-            mHaveMatrix = false;
-            mDsDx = mWin.mGlobalScale;
-            mDtDx = 0;
-            mDtDy = 0;
-            mDsDy = mWin.mGlobalScale;
+        mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+            mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
         }
-    }
-
-    private MagnificationSpec getMagnificationSpec() {
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
-            return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
-        }
-        return null;
+        mShownAlpha = mAlpha;
+        mHaveMatrix = false;
+        mDsDx = mWin.mGlobalScale;
+        mDtDx = 0;
+        mDtDy = 0;
+        mDsDy = mWin.mGlobalScale;
     }
 
     /**
@@ -1140,26 +1087,6 @@
             w.expandForSurfaceInsets(finalClipRect);
         }
 
-        // We may be applying a magnification spec to all windows,
-        // simulating a transformation in screen space, in which case
-        // we need to transform all other screen space values...including
-        // the final crop. This is kind of messed up and we should look
-        // in to actually transforming screen-space via a parent-layer.
-        // b/38322835
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null && !spec.isNop()) {
-            Matrix transform = mWin.mTmpMatrix;
-            RectF finalCrop = mService.mTmpRectF;
-            transform.reset();
-            transform.postScale(spec.scale, spec.scale);
-            transform.postTranslate(-spec.offsetX, -spec.offsetY);
-            transform.mapRect(finalCrop);
-            finalClipRect.top = (int) finalCrop.top;
-            finalClipRect.left = (int) finalCrop.left;
-            finalClipRect.right = (int) finalCrop.right;
-            finalClipRect.bottom = (int) finalCrop.bottom;
-        }
-
         return true;
     }
 
@@ -1517,7 +1444,6 @@
             mReportSurfaceResized = true;
             mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-            w.applyDimLayerIfNeeded();
         }
     }
 
@@ -1615,7 +1541,6 @@
                         mDtDy * w.mHScale * mExtraHScale,
                         mDsDy * w.mVScale * mExtraVScale,
                         recoveringMemory);
-            mSurfaceController.setLayer(mAnimLayer);
 
             if (prepared && mDrawState == HAS_DRAWN) {
                 if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index a214523..6746754 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    private SurfaceControlWithBackground mSurfaceControl;
+    SurfaceControlWithBackground mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -101,7 +101,8 @@
         mWindowSession = win.mSession;
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
-        final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+        final SurfaceControl.Builder b = win.makeSurface()
+                .setParent(win.getSurfaceControl())
                 .setName(name)
                 .setSize(w, h)
                 .setFormat(format)
@@ -245,25 +246,6 @@
         }
     }
 
-    void setLayer(int layer) {
-        if (mSurfaceControl != null) {
-            mService.openSurfaceTransaction();
-            try {
-                if (mAnimator.mWin.usesRelativeZOrdering()) {
-                    mSurfaceControl.setRelativeLayer(
-                            mAnimator.mWin.getParentWindow()
-                            .mWinAnimator.mSurfaceController.mSurfaceControl,
-                            -1);
-                } else {
-                    mSurfaceLayer = layer;
-                    mSurfaceControl.setLayer(layer);
-                }
-            } finally {
-                mService.closeSurfaceTransaction("setLayer");
-            }
-        }
-    }
-
     void setLayerStackInTransaction(int layerStack) {
         if (mSurfaceControl != null) {
             mSurfaceControl.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cd5e475..5081868 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -704,7 +704,7 @@
 
             // Create a new surface for the thumbnail
             WindowState window = appToken.findMainWindow();
-            final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+            final SurfaceControl surfaceControl = appToken.makeSurface()
                     .setName("thumbnail anim")
                     .setSize(dirty.width(), dirty.height())
                     .setFormat(PixelFormat.TRANSLUCENT)
@@ -712,7 +712,6 @@
                             window != null ? window.mOwnerUid : Binder.getCallingUid())
                     .build();
 
-            surfaceControl.setLayerStack(display.getLayerStack());
             if (SHOW_TRANSACTIONS) {
                 Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
             }
@@ -750,10 +749,13 @@
             anim.restrictDuration(MAX_ANIMATION_DURATION);
             anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
 
-            openingAppAnimator.updateThumbnailLayer();
             openingAppAnimator.thumbnail = surfaceControl;
             openingAppAnimator.thumbnailAnimation = anim;
             mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+            // We parent the thumbnail to the app token, and just place it
+            // on top of anything else in the app token.
+            surfaceControl.setLayer(Integer.MAX_VALUE);
         } catch (Surface.OutOfResourcesException e) {
             Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
                     + dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 5657f6c..6aa5101 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -67,6 +67,7 @@
             mWriteQueue.clear();
             mTraceFile.delete();
             try (OutputStream os = new FileOutputStream(mTraceFile)) {
+                mTraceFile.setReadable(true, false);
                 ProtoOutputStream proto = new ProtoOutputStream(os);
                 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
                 proto.flush();
@@ -147,7 +148,7 @@
     }
 
     static WindowTracing createDefaultAndStartLooper(Context context) {
-        File file = new File("/data/system/window_trace.proto");
+        File file = new File("/data/misc/wmtrace/wm_trace.pb");
         WindowTracing windowTracing = new WindowTracing(file);
         new Thread(windowTracing::loop, "window_tracing").start();
         return windowTracing;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 36e9e7f..4a2da37 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,7 +23,6 @@
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 6403a5a..df53fee 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -51,8 +51,9 @@
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
-using V1_2::ITunerCallback;
 using V1_1::ProgramListResult;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
 using utils::HalRevision;
 
 static mutex gContextMutex;
@@ -93,6 +94,7 @@
     wp<V1_1::IBroadcastRadio> mHalModule11;
     sp<V1_0::ITuner> mHalTuner;
     sp<V1_1::ITuner> mHalTuner11;
+    sp<V1_2::ITuner> mHalTuner12;
     sp<HalDeathRecipient> mHalDeathRecipient;
 
 private:
@@ -179,8 +181,11 @@
 
     ctx.mHalTuner = halTuner;
     ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
+    ctx.mHalTuner12 = V1_2::ITuner::castFrom(halTuner).withDefault(nullptr);
     ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
             "Provided tuner does not implement 1.1 HAL");
+    ALOGW_IF(ctx.mHalRev >= HalRevision::V1_2 && ctx.mHalTuner12 == nullptr,
+            "Provided tuner does not implement 1.2 HAL");
 
     ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
     halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
@@ -194,16 +199,21 @@
     return tuner;
 }
 
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
     lock_guard<mutex> lk(gContextMutex);
     return getHalTuner(getNativeContext(nativeContext));
 }
 
-sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
+static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
     lock_guard<mutex> lk(gContextMutex);
     return getNativeContext(nativeContext).mHalTuner11;
 }
 
+static sp<V1_2::ITuner> getHalTuner12(jlong nativeContext) {
+    lock_guard<mutex> lk(gContextMutex);
+    return getNativeContext(nativeContext).mHalTuner12;
+}
+
 sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
     return TunerCallback::getNativeCallback(env,
             env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
@@ -233,6 +243,7 @@
     ctx.mHalDeathRecipient = nullptr;
 
     ctx.mHalTuner11 = nullptr;
+    ctx.mHalTuner12 = nullptr;
     ctx.mHalTuner = nullptr;
 }
 
@@ -488,6 +499,48 @@
     convert::ThrowIfFailed(env, halResult);
 }
 
+static jobject nativeSetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jParameters) {
+    ALOGV("%s", __func__);
+
+    auto halTuner = getHalTuner12(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("Parameters are not supported with HAL < 1.2");
+        return nullptr;
+    }
+
+    JavaRef<jobject> jResults = nullptr;
+    auto parameters = convert::VendorInfoToHal(env, jParameters);
+    auto hidlResult = halTuner->setParameters(parameters,
+            [&](const hidl_vec<VendorKeyValue> results) {
+        jResults = convert::VendorInfoFromHal(env, results);
+    });
+
+    if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+    return jResults.release();
+}
+
+static jobject nativeGetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jKeys) {
+    ALOGV("%s", __func__);
+
+    auto halTuner = getHalTuner12(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("Parameters are not supported with HAL < 1.2");
+        return nullptr;
+    }
+
+    JavaRef<jobject> jResults = nullptr;
+    auto keys = convert::StringListToHal(env, jKeys);
+    auto hidlResult = halTuner->getParameters(keys,
+            [&](const hidl_vec<VendorKeyValue> parameters) {
+        jResults = convert::VendorInfoFromHal(env, parameters);
+    });
+
+    if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+    return jResults.release();
+}
+
 static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) {
     ALOGV("%s", __func__);
     auto halTuner = getHalTuner(nativeContext);
@@ -525,6 +578,8 @@
     { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
     { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
     { "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
+    { "nativeSetParameters", "(JLjava/util/Map;)Ljava/util/Map;", (void*)nativeSetParameters },
+    { "nativeGetParameters", "(JLjava/util/List;)Ljava/util/Map;", (void*)nativeGetParameters },
     { "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
 };
 
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index ed7c9c4..d624df6 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -70,6 +70,7 @@
         jmethodID onBackgroundScanAvailabilityChange;
         jmethodID onBackgroundScanComplete;
         jmethodID onProgramListChanged;
+        jmethodID onParametersUpdated;
     } TunerCallback;
 } gjni;
 
@@ -346,7 +347,10 @@
 Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
     ALOGV("%s", __func__);
 
-    // TODO(b/65862441): pass this callback to the front-end
+    mCallbackThread.enqueue([this, parameters](JNIEnv *env) {
+        auto jParameters = convert::VendorInfoFromHal(env, parameters);
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onParametersUpdated, jParameters.get());
+    });
 
     return {};
 }
@@ -437,6 +441,8 @@
             "onBackgroundScanComplete", "()V");
     gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onProgramListChanged", "()V");
+    gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
+            "onParametersUpdated", "(Ljava/util/Map;)V");
 
     auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
             gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8dfa14f..734ce79 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -34,6 +34,7 @@
 namespace utils = hardware::broadcastradio::utils;
 
 using hardware::Return;
+using hardware::hidl_string;
 using hardware::hidl_vec;
 using regions::RegionalBandConfig;
 
@@ -98,6 +99,11 @@
     } HashMap;
 
     struct {
+        jmethodID get;
+        jmethodID size;
+    } List;
+
+    struct {
         jmethodID put;
     } Map;
 
@@ -145,8 +151,21 @@
         jclass clazz;
         jmethodID cstor;
     } ParcelableException;
+
+    struct {
+        jclass clazz;
+    } String;
 } gjni;
 
+static jstring CastToString(JNIEnv *env, jobject obj) {
+    if (env->IsInstanceOf(obj, gjni.String.clazz)) {
+        return static_cast<jstring>(obj);
+    } else {
+        ALOGE("Cast failed, object is not a string");
+        return nullptr;
+    }
+}
+
 template <>
 bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
     return __ThrowIfFailedHidl(env, hidlResult);
@@ -250,12 +269,26 @@
 }
 
 static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
-    auto cstr = (jStr == nullptr) ? nullptr : env->GetStringUTFChars(jStr.get(), nullptr);
+    if (jStr == nullptr) return {};
+    auto cstr = env->GetStringUTFChars(jStr.get(), nullptr);
     std::string str(cstr);
     env->ReleaseStringUTFChars(jStr.get(), cstr);
     return str;
 }
 
+hidl_vec<hidl_string> StringListToHal(JNIEnv *env, jobject jList) {
+    auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size);
+    hidl_vec<hidl_string> list(len);
+
+    for (decltype(len) i = 0; i < len; i++) {
+        auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod(
+                jList, gjni.List.get, i)));
+        list[i] = StringFromJava(env, jString);
+    }
+
+    return list;
+}
+
 JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
     ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
 
@@ -275,7 +308,10 @@
 
     auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
             gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
-    LOG_FATAL_IF(jInfoArr == nullptr, "Converted array is null");
+    if (jInfoArr == nullptr) {
+        ALOGE("Converted array is null");
+        return {};
+    }
 
     auto len = env->GetArrayLength(jInfoArr.get());
     hidl_vec<VendorKeyValue> vec;
@@ -651,6 +687,10 @@
     gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
     gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
 
+    auto listClass = FindClassOrDie(env, "java/util/List");
+    gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+    gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I");
+
     auto mapClass = FindClassOrDie(env, "java/util/Map");
     gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
             "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@@ -713,6 +753,9 @@
     gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass);
     gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "<init>",
             "(Ljava/lang/Throwable;)V");
+
+    auto stringClass = FindClassOrDie(env, "java/lang/String");
+    gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass);
 }
 
 } // namespace android
diff --git a/services/core/jni/BroadcastRadio/convert.h b/services/core/jni/BroadcastRadio/convert.h
index 1fc75f0..b8c55c1 100644
--- a/services/core/jni/BroadcastRadio/convert.h
+++ b/services/core/jni/BroadcastRadio/convert.h
@@ -35,6 +35,8 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+hardware::hidl_vec<hardware::hidl_string> StringListToHal(JNIEnv *env, jobject jList);
+
 JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hardware::hidl_vec<V1_1::VendorKeyValue> &info);
 hardware::hidl_vec<V1_1::VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo);
 
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
deleted file mode 100644
index ad372de..0000000
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ /dev/null
@@ -1,1201 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_NDEBUG
-#undef LOG_TAG
-#define LOG_NDEBUG 0
-#define LOG_TAG "ContextHubService"
-
-#include <inttypes.h>
-#include <jni.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/endian.h>
-
-#include <chrono>
-#include <mutex>
-#include <queue>
-#include <unordered_map>
-#include <utility>
-
-#include <android-base/macros.h>
-#include <android/hardware/contexthub/1.0/IContexthub.h>
-#include <cutils/log.h>
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JNIHelp.h>
-
-using android::hardware::contexthub::V1_0::AsyncEventType;
-using android::hardware::contexthub::V1_0::ContextHub;
-using android::hardware::contexthub::V1_0::ContextHubMsg;
-using android::hardware::contexthub::V1_0::HubAppInfo;
-using android::hardware::contexthub::V1_0::IContexthub;
-using android::hardware::contexthub::V1_0::IContexthubCallback;
-using android::hardware::contexthub::V1_0::NanoAppBinary;
-using android::hardware::contexthub::V1_0::Result;
-using android::hardware::contexthub::V1_0::TransactionResult;
-
-using android::hardware::Return;
-
-using std::chrono::steady_clock;
-
-// If a transaction takes longer than this, we'll allow it to be
-// canceled by a new transaction.  Note we do _not_ automatically
-// cancel a transaction after this much time.  We can have a
-// legal transaction which takes longer than this amount of time,
-// as long as no other new transactions are attempted after this
-// time has expired.
-constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
-
-namespace android {
-
-constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
-
-// Important: this header is explicitly defined as little endian byte order, and
-// therefore may not match host endianness
-struct NanoAppBinaryHeader {
-    uint32_t headerVersion;        // 0x1 for this version
-    uint32_t magic;                // "NANO" (see NANOAPP_MAGIC in context_hub.h)
-    uint64_t appId;                // App Id, contains vendor id
-    uint32_t appVersion;           // Version of the app
-    uint32_t flags;                // Signed, encrypted
-    uint64_t hwHubType;            // Which hub type is this compiled for
-    uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
-    uint8_t targetChreApiMinorVersion;
-    uint8_t reserved[6];
-} __attribute__((packed));
-
-enum HubMessageType {
-    CONTEXT_HUB_APPS_ENABLE  = 1, // Enables loaded nano-app(s)
-    CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
-    CONTEXT_HUB_LOAD_APP     = 3, // Load a supplied app
-    CONTEXT_HUB_UNLOAD_APP   = 4, // Unload a specified app
-    CONTEXT_HUB_QUERY_APPS   = 5, // Query for app(s) info on hub
-    CONTEXT_HUB_QUERY_MEMORY = 6, // Query for memory info
-    CONTEXT_HUB_OS_REBOOT    = 7, // Request to reboot context HUB OS
-};
-
-constexpr jint OS_APP_ID = -1;
-constexpr jint INVALID_APP_ID = -2;
-
-constexpr jint MIN_APP_ID = 1;
-constexpr jint MAX_APP_ID = 128;
-
-constexpr size_t MSG_HEADER_SIZE = 4;
-constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
-constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
-constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
-constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
-
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
-
-jint getAppInstanceForAppId(uint64_t app_id);
-int onMessageReceipt(const uint32_t *header,
-                     size_t headerLen,
-                     const char *msg,
-                     size_t msgLen);
-void onHubReset(uint32_t hubId);
-void queryHubForApps(uint32_t hubId);
-void passOnOsResponse(uint32_t hubHandle,
-                      uint32_t msgType,
-                      TransactionResult result,
-                      const int8_t *additionalData,
-                      size_t additionalDataLen);
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle);
-void closeUnloadTxn(bool success);
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
-                               uint32_t hubHandle);
-
-struct JniInfo {
-    JavaVM *vm;
-    jclass contextHubInfoClass;
-    jclass contextHubServiceClass;
-    jclass memoryRegionsClass;
-
-    jobject jContextHubService;
-
-    jmethodID msgReceiptCallBack;
-
-    jmethodID contextHubInfoCtor;
-    jmethodID contextHubInfoSetId;
-    jmethodID contextHubInfoSetName;
-    jmethodID contextHubInfoSetVendor;
-    jmethodID contextHubInfoSetToolchain;
-    jmethodID contextHubInfoSetPlatformVersion;
-    jmethodID contextHubInfoSetStaticSwVersion;
-    jmethodID contextHubInfoSetToolchainVersion;
-    jmethodID contextHubInfoSetPeakMips;
-    jmethodID contextHubInfoSetStoppedPowerDrawMw;
-    jmethodID contextHubInfoSetSleepPowerDrawMw;
-    jmethodID contextHubInfoSetPeakPowerDrawMw;
-    jmethodID contextHubInfoSetSupportedSensors;
-    jmethodID contextHubInfoSetMemoryRegions;
-    jmethodID contextHubInfoSetMaxPacketLenBytes;
-
-    jmethodID contextHubServiceMsgReceiptCallback;
-    jmethodID contextHubServiceAddAppInstance;
-    jmethodID contextHubServiceDeleteAppInstance;
-};
-
-
-
-class TxnManager {
-public:
-    TxnManager() {
-        mData = nullptr;
-        mIsPending = false;
-    }
-
-    ~TxnManager() {
-        closeTxn();
-    }
-
-    int addTxn(HubMessageType txnIdentifier, void *txnData) {
-        std::lock_guard<std::mutex>lock(mLock);
-        if (mIsPending) {
-            ALOGW("Transaction already found pending when trying to add a new one.");
-            return -1;
-        }
-        mIsPending = true;
-        mFirstTimeTxnCanBeCanceled = steady_clock::now() + kMinTransactionCancelTime;
-        mData = txnData;
-        mIdentifier = txnIdentifier;
-
-        return 0;
-    }
-
-    int closeTxn() {
-        std::lock_guard<std::mutex>lock(mLock);
-        closeTxnUnlocked();
-        return 0;
-    }
-
-    bool isTxnPending() {
-        std::lock_guard<std::mutex>lock(mLock);
-        return mIsPending;
-    }
-
-    void closeAnyStaleTxns() {
-        std::lock_guard<std::mutex>lock(mLock);
-        if (mIsPending && steady_clock::now() >= mFirstTimeTxnCanBeCanceled) {
-            ALOGW("Stale transaction canceled");
-            closeTxnUnlocked();
-        }
-    }
-
-    int fetchTxnData(HubMessageType *id, void **data) {
-        if (id == nullptr || data == nullptr) {
-            ALOGW("Null Params isNull{id, data} {%d, %d}",
-                  id == nullptr ? 1 : 0,
-                  data == nullptr ? 1 : 0);
-            return -1;
-        }
-
-        std::lock_guard<std::mutex>lock(mLock);
-        if (!mIsPending) {
-            ALOGW("No Transactions pending");
-            return -1;
-        }
-
-        *id = mIdentifier;
-        *data = mData;
-        return 0;
-    }
-
- private:
-    bool mIsPending;            // Is a transaction pending
-    std::mutex mLock;           // mutex for manager
-    HubMessageType mIdentifier; // What are we doing
-    void *mData;                // Details
-    steady_clock::time_point mFirstTimeTxnCanBeCanceled;
-
-    // Only call this if you hold the lock.
-    void closeTxnUnlocked() {
-        mIsPending = false;
-        free(mData);
-        mData = nullptr;
-    }
-};
-
-
-struct ContextHubServiceCallback : IContexthubCallback {
-    uint32_t mContextHubId;
-
-    ContextHubServiceCallback(uint32_t hubId) {
-        mContextHubId = hubId;
-    }
-
-    virtual Return<void> handleClientMsg(const ContextHubMsg &msg) {
-        jint appHandle = getAppInstanceForAppId(msg.appName);
-        if (appHandle < 0) {
-            ALOGE("Filtering out message due to invalid App Instance.");
-        } else {
-            uint32_t msgHeader[MSG_HEADER_SIZE] = {};
-            msgHeader[HEADER_FIELD_MSG_TYPE] = msg.msgType;
-            msgHeader[HEADER_FIELD_HUB_HANDLE] = mContextHubId;
-            msgHeader[HEADER_FIELD_APP_INSTANCE] = appHandle;
-            onMessageReceipt(msgHeader,
-                             MSG_HEADER_SIZE,
-                             reinterpret_cast<const char *>(msg.msg.data()),
-                             msg.msg.size());
-        }
-
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleHubEvent(AsyncEventType evt) {
-        if (evt == AsyncEventType::RESTARTED) {
-            ALOGW("Context Hub handle %d restarted", mContextHubId);
-            onHubReset(mContextHubId);
-        } else {
-            ALOGW("Cannot handle event %u from hub %d", evt, mContextHubId);
-        }
-
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleTxnResult(uint32_t txnId,
-                                         TransactionResult result) {
-        ALOGI("Handle transaction result , hubId %" PRIu32 ", txnId %" PRIu32 ", result %" PRIu32,
-              mContextHubId,
-              txnId,
-              result);
-
-        switch(txnId) {
-            case CONTEXT_HUB_APPS_ENABLE:
-            case CONTEXT_HUB_APPS_DISABLE:
-                passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
-                break;
-
-            case CONTEXT_HUB_UNLOAD_APP:
-                closeUnloadTxn(result == TransactionResult::SUCCESS);
-                passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
-                break;
-
-            case CONTEXT_HUB_LOAD_APP:
-                {
-                    jint appInstanceHandle = INVALID_APP_ID;
-                    bool appRunningOnHub = (result == TransactionResult::SUCCESS);
-                    if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) {
-                        if (appRunningOnHub) {
-                            // Now we're in an odd situation.  Our nanoapp
-                            // is up and running on the Context Hub.  However,
-                            // something went wrong in our Service code so that
-                            // we're not able to properly track this nanoapp
-                            // in our Service code.  If we tell the Java layer
-                            // things are good, it's a lie because the handle
-                            // we give them will fail when used with the Service.
-                            // If we tell the Java layer this failed, it's kind
-                            // of a lie as well, since this nanoapp is running.
-                            //
-                            // We leave a more robust fix for later, and for
-                            // now just tell the user things have failed.
-                            //
-                            // TODO(b/30835981): Make this situation better.
-                            result = TransactionResult::FAILURE;
-                        }
-                    }
-
-                    passOnOsResponse(mContextHubId,
-                                     txnId,
-                                     result,
-                                     reinterpret_cast<int8_t *>(&appInstanceHandle),
-                                     sizeof(appInstanceHandle));
-                    break;
-                }
-
-            default:
-                ALOGI("unrecognized transction id %" PRIu32, txnId);
-                break;
-        }
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleAppsInfo(
-            const android::hardware::hidl_vec<HubAppInfo>& apps) {
-        TransactionResult result = TransactionResult::SUCCESS;
-        handleQueryAppsResponse(apps,mContextHubId);
-        passOnOsResponse(mContextHubId, CONTEXT_HUB_QUERY_APPS, result, nullptr, 0);
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) {
-        ALOGI("Handle app aport called from %" PRIx64 " with abort code %" PRIu32,
-            appId,
-            abortCode);
-
-        // TODO: Plumb this to the clients interested in this app
-        return android::hardware::Void();
-    }
-
-    void setContextHubId(uint32_t id) {
-        mContextHubId = id;
-    }
-
-    uint32_t getContextHubId() {
-        return(mContextHubId);
-    }
-};
-
-struct AppInstanceInfo {
-    HubAppInfo appInfo;          // returned from the HAL
-    uint64_t truncName;          // Possibly truncated name for logging
-    uint32_t hubHandle;          // Id of the hub this app is on
-    jint instanceId;             // system wide unique instance id - assigned
-};
-
-struct ContextHubInfo {
-    int numHubs;
-    Vector<ContextHub> hubs;
-    sp<IContexthub> contextHub;
-};
-
-struct ContextHubServiceDb {
-    int initialized;
-    ContextHubInfo hubInfo;
-    JniInfo jniInfo;
-    std::queue<jint> freeIds;
-    std::unordered_map<jint, AppInstanceInfo> appInstances;
-    TxnManager txnManager;
-    std::vector<ContextHubServiceCallback *> regCallBacks;
-};
-
-ContextHubServiceDb db;
-
-bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) {
-    if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) {
-        return false;
-    } else {
-        *hubId = db.hubInfo.hubs[hubHandle].hubId;
-        return true;
-    }
-}
-
-int getHubHandleForAppInstance(jint id) {
-    if (!db.appInstances.count(id)) {
-        ALOGD("%s: Cannot find app for app instance %" PRId32,
-              __FUNCTION__,
-              id);
-        return -1;
-    }
-
-    return db.appInstances[id].hubHandle;
-}
-
-jint getAppInstanceForAppId(uint64_t app_id) {
-    auto end = db.appInstances.end();
-    for (auto current = db.appInstances.begin(); current != end; ++current) {
-        if (current->second.appInfo.appId == app_id) {
-            return current->first;
-        }
-    }
-    ALOGD("Cannot find app for app id %" PRIu64 ".", app_id);
-    return -1;
-}
-
-uint64_t getAppIdForAppInstance(jint id) {
-    if (!db.appInstances.count(id)) {
-        return INVALID_APP_ID;
-    }
-    return db.appInstances[id].appInfo.appId;
-}
-
-void queryHubForApps(uint32_t hubId) {
-    Result r = db.hubInfo.contextHub->queryApps(hubId);
-    ALOGD("Sent query for apps to hub %" PRIu32 " with result %" PRIu32, hubId, r);
-}
-
-void sendQueryForApps() {
-    for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
-        queryHubForApps(db.hubInfo.hubs[i].hubId);
-    }
-}
-
-int returnId(jint id) {
-    // Note : This method is not thread safe.
-    // id returned is guaranteed to be in use
-    if (id >= 0) {
-        db.freeIds.push(id);
-        return 0;
-    }
-
-    return -1;
-}
-
-jint generateId() {
-    // Note : This method is not thread safe.
-    jint retVal = -1;
-
-    if (!db.freeIds.empty()) {
-        retVal = db.freeIds.front();
-        db.freeIds.pop();
-    }
-
-    return retVal;
-}
-
-jint addAppInstance(const HubAppInfo *appInfo, uint32_t hubHandle,
-        jint appInstanceHandle, JNIEnv *env) {
-    // Not checking if the apps are indeed distinct
-    AppInstanceInfo entry;
-    assert(appInfo);
-
-
-    entry.appInfo = *appInfo;
-
-    entry.instanceId = appInstanceHandle;
-    entry.truncName = appInfo->appId;
-    entry.hubHandle = hubHandle;
-    db.appInstances[appInstanceHandle] = entry;
-    // Finally - let the service know of this app instance, to populate
-    // the Java cache.
-    env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceAddAppInstance,
-                       hubHandle, entry.instanceId,
-                       entry.truncName,
-                       entry.appInfo.version);
-
-    const char *action = (db.appInstances.count(appInstanceHandle) == 0) ? "Added" : "Updated";
-    ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
-          " as appInstance %" PRId32, action, entry.truncName,
-          entry.hubHandle, appInstanceHandle);
-
-    return appInstanceHandle;
-}
-
-int deleteAppInstance(jint id, JNIEnv *env) {
-    bool fullyDeleted = true;
-
-    if (db.appInstances.count(id)) {
-        db.appInstances.erase(id);
-    } else {
-        ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id);
-        fullyDeleted = false;
-    }
-    returnId(id);
-
-    if ((env == nullptr) ||
-        (env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceDeleteAppInstance,
-                       id) != 0)) {
-        ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id);
-        fullyDeleted = false;
-    }
-
-    if (fullyDeleted) {
-        ALOGI("Deleted App id : %" PRId32, id);
-        return 0;
-    }
-    return -1;
-}
-
-int startLoadAppTxn(uint64_t appId, int hubHandle) {
-    AppInstanceInfo *txnInfo = new AppInstanceInfo();
-    jint instanceId = generateId();
-
-    if (!txnInfo || instanceId < 0) {
-        returnId(instanceId);
-        delete txnInfo;
-        return -1;
-    }
-
-    txnInfo->truncName = appId;
-    txnInfo->hubHandle = hubHandle;
-    txnInfo->instanceId = instanceId;
-
-    txnInfo->appInfo.appId = appId;
-    txnInfo->appInfo.version = -1; // Awaited
-
-    if (db.txnManager.addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) {
-        returnId(instanceId);
-        delete txnInfo;
-        return -1;
-    }
-
-    return 0;
-}
-
-int startUnloadAppTxn(jint appInstanceHandle) {
-    jint *txnData = new(jint);
-    if (!txnData) {
-        ALOGW("Cannot allocate memory to start unload transaction");
-        return -1;
-    }
-
-    *txnData = appInstanceHandle;
-
-    if (db.txnManager.addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) {
-        delete txnData;
-        ALOGW("Cannot start transaction to unload app");
-        return -1;
-    }
-
-    return 0;
-}
-
-void getHubsCb(const ::android::hardware::hidl_vec<ContextHub>& hubs)  {
-    for (size_t i = 0; i < hubs.size(); i++) {
-        db.hubInfo.hubs.push_back(hubs[i]);
-    }
-}
-
-void initContextHubService() {
-    db.hubInfo.numHubs = 0;
-
-    db.hubInfo.contextHub = IContexthub::getService();
-
-    if (db.hubInfo.contextHub == nullptr) {
-        ALOGE("Could not load context hub hal");
-    } else {
-        ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE");
-    }
-
-    // Prep for storing app info
-    for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
-        db.freeIds.push(i);
-    }
-
-    if (db.hubInfo.contextHub != nullptr) {
-        std::function<void(const ::android::hardware::hidl_vec<ContextHub>& hubs)> f = getHubsCb;
-        if(!db.hubInfo.contextHub->getHubs(f).isOk()) {
-            ALOGW("GetHubs Failed! transport error.");
-            return;
-        };
-
-        int retNumHubs = db.hubInfo.hubs.size();
-        ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
-        db.hubInfo.numHubs = retNumHubs;
-
-        for (int i = 0; i < db.hubInfo.numHubs; i++) {
-            ALOGI("Subscribing to hubHandle %d", i);
-
-            ContextHubServiceCallback *callBackPtr =
-                new ContextHubServiceCallback(db.hubInfo.hubs[i].hubId);
-            db.hubInfo.contextHub->registerCallback(db.hubInfo.hubs[i].hubId,
-                                                    callBackPtr);
-            db.regCallBacks.push_back(callBackPtr);
-        }
-
-        sendQueryForApps();
-
-    } else {
-        ALOGW("No Context Hub Module present");
-    }
-}
-
-void onHubReset(uint32_t hubId) {
-    TransactionResult result = TransactionResult::SUCCESS;
-    db.txnManager.closeTxn();
-    // TODO : Expose this through an api
-    passOnOsResponse(hubId, CONTEXT_HUB_OS_REBOOT, result, nullptr, 0);
-    queryHubForApps(hubId);
-}
-
-int onMessageReceipt(const uint32_t *header,
-                     size_t headerLen,
-                     const char *msg,
-                     size_t msgLen) {
-    JNIEnv *env;
-
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-      return -1;
-    }
-
-    jbyteArray jmsg = env->NewByteArray(msgLen);
-    if (jmsg == nullptr) {
-        ALOGW("Can't allocate %zu byte array", msgLen);
-        return -1;
-    }
-    jintArray jheader = env->NewIntArray(headerLen);
-    if (jheader == nullptr) {
-        env->DeleteLocalRef(jmsg);
-        ALOGW("Can't allocate %zu int array", headerLen);
-        return -1;
-    }
-
-    env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<const jbyte *>(msg));
-    env->SetIntArrayRegion(jheader, 0, headerLen, reinterpret_cast<const jint *>(header));
-
-    int ret = (env->CallIntMethod(db.jniInfo.jContextHubService,
-                                  db.jniInfo.contextHubServiceMsgReceiptCallback,
-                                  jheader,
-                                  jmsg) != 0);
-    env->DeleteLocalRef(jmsg);
-    env->DeleteLocalRef(jheader);
-
-    return ret;
-}
-
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
-                               uint32_t hubHandle) {
-    JNIEnv *env;
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-            return -1;
-    }
-
-    int numApps = apps.size();
-
-    // We use this information to sync our JNI and Java caches of nanoapp info.
-    // We want to accomplish two things here:
-    // 1) Remove entries from our caches which are stale, and pertained to
-    //    apps no longer running on Context Hub.
-    // 2) Populate our caches with the latest information of all these apps.
-
-    // We make a couple of assumptions here:
-    // A) The JNI and Java caches are in sync with each other (this isn't
-    //    necessarily true; any failure of a single call into Java land to
-    //    update its cache will leave that cache in a bad state.  For NYC,
-    //    we're willing to tolerate this for now).
-    // B) The total number of apps is relatively small, so horribly inefficent
-    //    algorithms aren't too painful.
-    // C) We're going to call this relatively infrequently, so its inefficency
-    //    isn't a big impact.
-
-
-    // (1).  Looking for stale cache entries.  Yes, this is O(N^2).  See
-    // assumption (B).  Per assumption (A), it is sufficient to iterate
-    // over just the JNI cache.
-    auto end = db.appInstances.end();
-    for (auto current = db.appInstances.begin(); current != end; ) {
-        AppInstanceInfo cacheEntry = current->second;
-        // We perform our iteration here because if we call
-        // delete_app_instance() below, it will erase() this entry.
-        current++;
-        bool entryIsStale = true;
-        for (int i = 0; i < numApps; i++) {
-            if (apps[i].appId == cacheEntry.appInfo.appId) {
-                // We found a match; this entry is current.
-                entryIsStale = false;
-                break;
-            }
-        }
-
-        if (entryIsStale) {
-            deleteAppInstance(cacheEntry.instanceId, env);
-        }
-    }
-
-    // (2).  Update our caches with the latest.
-    for (int i = 0; i < numApps; i++) {
-        // We will only have one instance of the app
-        // TODO : Change this logic once we support multiple instances of the same app
-        jint appInstance = getAppInstanceForAppId(apps[i].appId);
-        if (appInstance == -1) {
-            // This is a previously unknown app, let's allocate an "id" for it.
-            appInstance = generateId();
-        }
-        addAppInstance(&apps[i], hubHandle, appInstance, env);
-    }
-    return 0;
-}
-
-// TODO(b/30807327): Do not use raw bytes for additional data.  Use the
-//     JNI interfaces for the appropriate types.
-void passOnOsResponse(uint32_t hubHandle,
-                      uint32_t msgType,
-                      TransactionResult result,
-                      const int8_t *additionalData,
-                      size_t additionalDataLen) {
-    JNIEnv *env;
-
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-        ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32,
-              msgType);
-        return;
-    }
-
-    uint32_t header[MSG_HEADER_SIZE];
-    memset(header, 0, sizeof(header));
-
-    if (!additionalData) {
-        additionalDataLen = 0; // clamp
-    }
-    int msgLen = 1 + additionalDataLen;
-
-    int8_t *msg = new int8_t[msgLen];
-
-    if (!msg) {
-        ALOGW("Unexpected : Ran out of memory, cannot send response");
-        return;
-    }
-
-    header[HEADER_FIELD_MSG_TYPE] = msgType;
-    header[HEADER_FIELD_MSG_VERSION] = 0;
-    header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
-    header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
-
-    // Due to API constraints, at the moment we can't change the fact that
-    // we're changing our 4-byte response to a 1-byte value.  But we can prevent
-    // the possible change in sign (and thus meaning) that would happen from
-    // a naive cast.  Further, we can log when we're losing part of the value.
-    // TODO(b/30918279): Don't truncate this result.
-    int8_t truncatedResult;
-    truncatedResult = static_cast<int8_t>(result);
-    msg[0] = truncatedResult;
-
-    if (additionalData) {
-        memcpy(&msg[1], additionalData, additionalDataLen);
-    }
-
-    jbyteArray jmsg = env->NewByteArray(msgLen);
-    jintArray jheader = env->NewIntArray(arraysize(header));
-
-    env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<jbyte *>(msg));
-    env->SetIntArrayRegion(jheader, 0, arraysize(header), reinterpret_cast<jint *>(header));
-
-    ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
-          header[HEADER_FIELD_MSG_TYPE],
-          header[HEADER_FIELD_APP_INSTANCE],
-          header[HEADER_FIELD_HUB_HANDLE]);
-
-    env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceMsgReceiptCallback,
-                       jheader,
-                       jmsg);
-
-    env->DeleteLocalRef(jmsg);
-    env->DeleteLocalRef(jheader);
-
-    delete[] msg;
-}
-
-void closeUnloadTxn(bool success) {
-    void *txnData = nullptr;
-    HubMessageType txnId;
-
-    if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
-        txnId == CONTEXT_HUB_UNLOAD_APP) {
-        JNIEnv *env;
-        if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-            ALOGW("Could not attach to JVM !");
-            env = nullptr;
-        }
-        jint handle = *reinterpret_cast<jint *>(txnData);
-        deleteAppInstance(handle, env);
-    } else {
-        ALOGW("Could not unload the app successfully ! success %d, txnData %p",
-              success,
-              txnData);
-    }
-
-    db.txnManager.closeTxn();
-}
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle) {
-    void *txnData;
-    HubMessageType txnId;
-
-    if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
-        txnId == CONTEXT_HUB_LOAD_APP) {
-        AppInstanceInfo *info = static_cast<AppInstanceInfo *>(txnData);
-        *appInstanceHandle = info->instanceId;
-
-        JNIEnv *env;
-        if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) {
-            addAppInstance(&info->appInfo, info->hubHandle, info->instanceId, env);
-        } else {
-            ALOGW("Could not attach to JVM !");
-            success = false;
-        }
-        // While we just called addAppInstance above, our info->appInfo was
-        // incomplete (for example, the 'version' is hardcoded to -1).  So we
-        // trigger an additional query to the CHRE, so we'll be able to get
-        // all the app "info", and have our JNI and Java caches with the
-        // full information.
-        sendQueryForApps();
-    } else {
-        ALOGW("Could not load the app successfully ! Unexpected failure");
-        *appInstanceHandle = INVALID_APP_ID;
-        success = false;
-    }
-
-    db.txnManager.closeTxn();
-    return success;
-}
-
-int initJni(JNIEnv *env, jobject instance) {
-    if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
-        return -1;
-    }
-
-    db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
-
-    db.jniInfo.contextHubInfoClass =
-            env->FindClass("android/hardware/location/ContextHubInfo");
-    db.jniInfo.contextHubServiceClass =
-            env->FindClass("com/android/server/location/ContextHubService");
-
-    db.jniInfo.memoryRegionsClass =
-            env->FindClass("android/hardware/location/MemoryRegion");
-
-    db.jniInfo.contextHubInfoCtor =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
-    db.jniInfo.contextHubInfoSetId =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V");
-    db.jniInfo.contextHubInfoSetName =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetVendor =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setVendor",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetToolchain =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setToolchain",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetPlatformVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPlatformVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetStaticSwVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setStaticSwVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetToolchainVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setToolchainVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetPeakMips =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPeakMips",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetStoppedPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setStoppedPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetSleepPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setSleepPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetPeakPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPeakPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetSupportedSensors =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setSupportedSensors",
-                             "([I)V");
-    db.jniInfo.contextHubInfoSetMemoryRegions =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setMemoryRegions",
-                             "([Landroid/hardware/location/MemoryRegion;)V");
-    db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
-             env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                              "setMaxPacketLenBytes",
-                              "(I)V");
-    db.jniInfo.contextHubServiceMsgReceiptCallback =
-            env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                             "onMessageReceipt",
-                             "([I[B)I");
-    db.jniInfo.contextHubInfoSetName =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setName",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubServiceAddAppInstance =
-                 env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                                  "addAppInstance",
-                                  "(IIJI)I");
-    db.jniInfo.contextHubServiceDeleteAppInstance =
-                 env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                                  "deleteAppInstance",
-                                  "(I)I");
-
-    return 0;
-}
-
-jobject constructJContextHubInfo(JNIEnv *env, const ContextHub &hub) {
-    jstring jstrBuf;
-    jintArray jintBuf;
-    jobjectArray jmemBuf;
-
-    jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass,
-                                  db.jniInfo.contextHubInfoCtor);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
-
-    jstrBuf = env->NewStringUTF(hub.name.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    jstrBuf = env->NewStringUTF(hub.vendor.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub.platformVersion);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub.toolchainVersion);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub.peakMips);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
-                        hub.stoppedPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
-                        hub.sleepPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
-                        hub.peakPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
-                        hub.maxSupportedMsgLen);
-
-
-    jintBuf = env->NewIntArray(hub.connectedSensors.size());
-    int *connectedSensors = new int[hub.connectedSensors.size()];
-
-    if (!connectedSensors) {
-      ALOGW("Cannot allocate memory! Unexpected");
-      assert(false);
-    } else {
-      for (unsigned int i = 0; i < hub.connectedSensors.size(); i++) {
-        // TODO :: Populate connected sensors.
-        //connectedSensors[i] = hub.connectedSensors[i].sensorType;
-        connectedSensors[i] = 0;
-      }
-    }
-
-    env->SetIntArrayRegion(jintBuf, 0, hub.connectedSensors.size(),
-                           connectedSensors);
-
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
-    env->DeleteLocalRef(jintBuf);
-
-    // We are not getting the memory regions from the CH Hal - change this when it is available
-    jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
-    // Note the zero size above. We do not need to set any elements
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
-    env->DeleteLocalRef(jmemBuf);
-
-
-    delete[] connectedSensors;
-    return jHub;
-}
-
-jobjectArray nativeInitialize(JNIEnv *env, jobject instance) {
-    jobject hub;
-    jobjectArray retArray;
-
-    if (initJni(env, instance) < 0) {
-        return nullptr;
-    }
-
-    initContextHubService();
-
-    if (db.hubInfo.numHubs > 1) {
-        ALOGW("Clamping the number of hubs to 1");
-        db.hubInfo.numHubs = 1;
-    }
-
-    retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
-
-    for(int i = 0; i < db.hubInfo.numHubs; i++) {
-        hub = constructJContextHubInfo(env, db.hubInfo.hubs[i]);
-        env->SetObjectArrayElement(retArray, i, hub);
-    }
-
-    return retArray;
-}
-
-Result sendLoadNanoAppRequest(uint32_t hubId,
-                              jbyte *data,
-                              size_t dataBufferLength) {
-    auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
-    Result result;
-
-    if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
-        ALOGE("Got short NanoApp, length %zu", dataBufferLength);
-        result = Result::BAD_PARAMS;
-    } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
-        ALOGE("Got unexpected NanoApp header version %" PRIu32,
-              letoh32(header->headerVersion));
-        result = Result::BAD_PARAMS;
-    } else {
-        NanoAppBinary nanoapp;
-
-        // Data from the common nanoapp header goes into explicit fields
-        nanoapp.appId      = letoh64(header->appId);
-        nanoapp.appVersion = letoh32(header->appVersion);
-        nanoapp.flags      = letoh32(header->flags);
-        nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
-        nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
-
-        // Everything past the header goes in customBinary
-        auto dataBytes = reinterpret_cast<const uint8_t *>(data);
-        std::vector<uint8_t> customBinary(
-            dataBytes + sizeof(NanoAppBinaryHeader),
-            dataBytes + dataBufferLength);
-        nanoapp.customBinary = std::move(customBinary);
-
-        ALOGW("Calling Load NanoApp on hub %d", hubId);
-        result = db.hubInfo.contextHub->loadNanoApp(hubId,
-                                                    nanoapp,
-                                                    CONTEXT_HUB_LOAD_APP);
-    }
-
-    return result;
-}
-
-jint nativeSendMessage(JNIEnv *env,
-                       jobject instance,
-                       jintArray header_,
-                       jbyteArray data_) {
-    // With the new binderized HAL definition, this function can be made much simpler.
-    // All the magic can be removed. This is not however needed for the default implementation
-    // TODO :: Change the JNI interface to conform to the new HAL interface and clean up this
-    // function
-    jint retVal = -1; // Default to failure
-
-    jint *header = env->GetIntArrayElements(header_, 0);
-    size_t numHeaderElements = env->GetArrayLength(header_);
-    jbyte *data = env->GetByteArrayElements(data_, 0);
-    size_t dataBufferLength = env->GetArrayLength(data_);
-
-    if (numHeaderElements < MSG_HEADER_SIZE) {
-        ALOGW("Malformed header len");
-        return -1;
-    }
-
-    jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
-    uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
-    int hubHandle = -1;
-    uint64_t appId;
-
-    if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-        hubHandle = getHubHandleForAppInstance(appInstanceHandle);
-    } else if (msgType == CONTEXT_HUB_LOAD_APP) {
-        if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) {
-            return -1;
-        }
-        uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO];
-        uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI];
-        appId = appIdHi << 32 | appIdLo;
-
-        hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-    } else {
-        hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-    }
-
-    uint32_t hubId = -1;
-    if (!getHubIdForHubHandle(hubHandle, &hubId)) {
-        ALOGD("Invalid hub Handle %d", hubHandle);
-        return -1;
-    }
-
-    if (msgType == CONTEXT_HUB_LOAD_APP ||
-        msgType == CONTEXT_HUB_UNLOAD_APP) {
-
-        db.txnManager.closeAnyStaleTxns();
-
-        if (db.txnManager.isTxnPending()) {
-            // TODO : There is a race conditio
-            ALOGW("Cannot load or unload app while a transaction is pending !");
-            return -1;
-        } else if (msgType == CONTEXT_HUB_LOAD_APP) {
-            if (startLoadAppTxn(appId, hubHandle) != 0) {
-                ALOGW("Cannot Start Load Transaction");
-                return -1;
-            }
-        } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-            if (startUnloadAppTxn(appInstanceHandle) != 0) {
-                ALOGW("Cannot Start UnLoad Transaction");
-                return -1;
-            }
-        }
-    }
-
-    Result result;
-
-    if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-        ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32,
-              db.appInstances[appInstanceHandle].appInfo.appId,
-              hubId);
-        result = db.hubInfo.contextHub->unloadNanoApp(
-                hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP);
-    } else {
-        if (appInstanceHandle == OS_APP_ID) {
-            if (msgType == CONTEXT_HUB_LOAD_APP) {
-                result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
-            } else if (msgType == CONTEXT_HUB_QUERY_APPS) {
-                result = db.hubInfo.contextHub->queryApps(hubId);
-            } else {
-                ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
-                result = Result::BAD_PARAMS;
-            }
-        } else {
-            appId = getAppIdForAppInstance(appInstanceHandle);
-            if (appId == static_cast<uint64_t>(INVALID_APP_ID)) {
-                ALOGD("Cannot find application instance %d", appInstanceHandle);
-                result = Result::BAD_PARAMS;
-            } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) {
-                ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)",
-                      hubHandle,
-                      getHubHandleForAppInstance(appInstanceHandle));
-                result = Result::BAD_PARAMS;
-            } else {
-                ContextHubMsg msg;
-                msg.appName = appId;
-                msg.msgType = msgType;
-                msg.msg.setToExternal((unsigned char *)data, dataBufferLength);
-
-                ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32,
-                       msgType,
-                       dataBufferLength,
-                       appId,
-                       hubId);
-                result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg);
-            }
-        }
-    }
-
-    if (result != Result::OK) {
-        ALOGD("Send Message failure - %d", result);
-        if (msgType == CONTEXT_HUB_LOAD_APP) {
-            jint ignored;
-            closeLoadTxn(false, &ignored);
-        } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-            closeUnloadTxn(false);
-        }
-    } else {
-        retVal = 0;
-    }
-
-    env->ReleaseIntArrayElements(header_, header, 0);
-    env->ReleaseByteArrayElements(data_, data, 0);
-
-    return retVal;
-}
-
-//--------------------------------------------------------------------------------------------------
-//
-const JNINativeMethod gContextHubServiceMethods[] = {
-    {"nativeInitialize",
-            "()[Landroid/hardware/location/ContextHubInfo;",
-            reinterpret_cast<void*>(nativeInitialize)},
-    {"nativeSendMessage",
-            "([I[B)I",
-            reinterpret_cast<void*>(nativeSendMessage)}
-};
-
-int register_android_server_location_ContextHubService(JNIEnv *env)
-{
-    RegisterMethodsOrDie(env, "com/android/server/location/ContextHubService",
-            gContextHubServiceMethods, NELEM(gContextHubServiceMethods));
-
-    return 0;
-}
-
-}//namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 2f45181..46d5043 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,6 @@
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_vr_VrManagerService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_location_ContextHubService(JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
@@ -82,7 +81,6 @@
     register_android_server_vr_VrManagerService(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
-    register_android_server_location_ContextHubService(env);
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_connectivity_Vpn(env);
     register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 66f5770..c145e82 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1872,8 +1872,8 @@
     }
 
     @Test
-    public void testStats_updatedOnExpansion() throws Exception {
-        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+    public void testStats_updatedOnUserExpansion() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
@@ -1883,6 +1883,17 @@
     }
 
     @Test
+    public void testStats_notUpdatedOnAutoExpansion() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+        assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
+        assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+    }
+
+    @Test
     public void testStats_updatedOnViewSettings() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76ab..9a5ebed 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index d9fac87..6938e0f 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -99,10 +99,12 @@
         assertNotNull(mInjector.mSensorListener);
         assertNotNull(mInjector.mSettingsObserver);
         assertNotNull(mInjector.mBroadcastReceiver);
+        assertTrue(mInjector.mIdleScheduled);
         mTracker.stop();
         assertNull(mInjector.mSensorListener);
         assertNull(mInjector.mSettingsObserver);
         assertNull(mInjector.mBroadcastReceiver);
+        assertFalse(mInjector.mIdleScheduled);
     }
 
     @Test
@@ -399,6 +401,52 @@
     }
 
     @Test
+    public void testWritePrunesOldEvents() throws Exception {
+        final int brightness = 20;
+
+        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+        mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
+        mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+
+        startTracker(mTracker);
+        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+                batteryChangeEvent(30, 100));
+        mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
+        mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
+        mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f));
+        final long sensorTime = mInjector.currentTimeMillis();
+        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+                Settings.System.SCREEN_BRIGHTNESS));
+
+        // 31 days later
+        mInjector.incrementTime(TimeUnit.DAYS.toMillis(31));
+        mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
+        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+                Settings.System.SCREEN_BRIGHTNESS));
+        final long eventTime = mInjector.currentTimeMillis();
+
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        assertEquals(2, events.size());
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        mTracker.writeEventsLocked(baos);
+        events = mTracker.getEvents(0).getList();
+        mTracker.stop();
+
+        assertEquals(1, events.size());
+        BrightnessChangeEvent event = events.get(0);
+        assertEquals(eventTime, event.timeStamp);
+
+        // We will keep one of the old sensor events because we keep 1 event outside the window.
+        assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f);
+        assertArrayEquals(new long[] {sensorTime, eventTime}, event.luxTimestamps);
+        assertEquals(brightness, event.brightness);
+        assertEquals(0.3, event.batteryLevel, 0.01f);
+        assertTrue(event.nightMode);
+        assertEquals(3339, event.colorTemperature);
+    }
+
+    @Test
     public void testParcelUnParcel() {
         Parcel parcel = Parcel.obtain();
         BrightnessChangeEvent event = new BrightnessChangeEvent();
@@ -516,6 +564,7 @@
         long mCurrentTimeMillis = System.currentTimeMillis();
         long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
         Handler mHandler;
+        boolean mIdleScheduled;
 
         public TestInjector(Handler handler) {
             mHandler = handler;
@@ -636,5 +685,14 @@
             focusedStack.topActivity = new ComponentName("a.package", "a.class");
             return focusedStack;
         }
+
+        public void scheduleIdleJob(Context context) {
+            // Don't actually schedule jobs during unit tests.
+            mIdleScheduled = true;
+        }
+
+        public void cancelIdleJob(Context context) {
+            mIdleScheduled = false;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 05c4853..2224de5 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -31,6 +31,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Base64;
+import android.webkit.UserPackage;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
@@ -1628,10 +1629,10 @@
         newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
         PackageInfo currentSdkPackage = createPackageInfo("currentTargetSdkPackage",
             true /* enabled */, true /* valid */, true /* installed */);
-        currentSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1+1;
+        currentSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK;
         PackageInfo oldSdkPackage = createPackageInfo("oldTargetSdkPackage",
             true /* enabled */, true /* valid */, true /* installed */);
-        oldSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+        oldSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK - 1;
 
         WebViewProviderInfo newSdkProviderInfo =
                 new WebViewProviderInfo(newSdkPackage.packageName, "", true, false, null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 6060881..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@
             controller.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+            controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            mDisplayContent.onPendingTransactionApplied();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index bb88264..d9ab5c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@
 @RunWith(AndroidJUnit4.class)
 public class AppWindowTokenTests extends WindowTestsBase {
 
+    TaskStack mStack;
+    Task mTask;
+    WindowTestUtils.TestAppWindowToken mToken;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mStack = createTaskStackOnDisplay(mDisplayContent);
+        mTask = createTaskInStack(mStack, 0 /* userId */);
+        mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+        mTask.addChild(mToken, 0);
+    }
+
     @Test
     @Presubmit
     public void testAddWindow_Order() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertEquals(0, mToken.getWindowsCount());
 
-        assertEquals(0, token.getWindowsCount());
-
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
-        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+        final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
                 "startingWin");
-        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
-        final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+        final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
 
         // Should not contain the windows that were added above.
-        assertEquals(4, token.getWindowsCount());
-        assertTrue(token.hasWindow(win1));
-        assertTrue(token.hasWindow(startingWin));
-        assertTrue(token.hasWindow(baseWin));
-        assertTrue(token.hasWindow(win4));
+        assertEquals(4, mToken.getWindowsCount());
+        assertTrue(mToken.hasWindow(win1));
+        assertTrue(mToken.hasWindow(startingWin));
+        assertTrue(mToken.hasWindow(baseWin));
+        assertTrue(mToken.hasWindow(win4));
 
         // The starting window should be on-top of all other windows.
-        assertEquals(startingWin, token.getLastChild());
+        assertEquals(startingWin, mToken.getLastChild());
 
         // The base application window should be below all other windows.
-        assertEquals(baseWin, token.getFirstChild());
-        token.removeImmediately();
+        assertEquals(baseWin, mToken.getFirstChild());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testFindMainWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.findMainWindow());
 
-        assertNull(token.findMainWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        assertEquals(window1, token.findMainWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+        assertEquals(window1, mToken.findMainWindow());
         window1.mAnimatingExit = true;
-        assertEquals(window1, token.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
-        assertEquals(window2, token.findMainWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.findMainWindow());
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+        assertEquals(window2, mToken.findMainWindow());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testGetTopFullscreenWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.getTopFullscreenWindow());
 
-        assertNull(token.getTopFullscreenWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
-        final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
-        assertEquals(window12, token.getTopFullscreenWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+        final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+        assertEquals(window12, mToken.getTopFullscreenWindow());
         window12.mAttrs.width = 500;
-        assertEquals(window11, token.getTopFullscreenWindow());
+        assertEquals(window11, mToken.getTopFullscreenWindow());
         window11.mAttrs.width = 500;
-        assertEquals(window1, token.getTopFullscreenWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.getTopFullscreenWindow());
+        mToken.removeImmediately();
     }
 
     @Test
@@ -124,27 +130,21 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.resizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
-        final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
         performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@
     @Test
     @Presubmit
     public void testGetOrientation() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        token.setFillsParent(false);
+        mToken.setFillsParent(false);
         // Can specify orientation if app doesn't fill parent.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
 
-        token.setFillsParent(true);
-        token.hidden = true;
-        token.sendingToBottom = true;
+        mToken.setFillsParent(true);
+        mToken.hidden = true;
+        mToken.sendingToBottom = true;
         // Can not specify orientation if app isn't visible even though it fills parent.
-        assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
 
-        token.sendingToBottom = false;
-        token.setIsOnTop(true);
-        // Allow for token to provide orientation hidden if on top and not being sent to bottom.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        mToken.sendingToBottom = false;
+        mToken.setIsOnTop(true);
+        // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
     }
 
     @Test
     @Presubmit
     public void testKeyguardFlagsDuringRelaunch() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
 
         // Add window with show when locked flag
-        token.addWindow(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.addWindow(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Start relaunching
-        token.startRelaunching();
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.startRelaunching();
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Remove window and make sure that we still report back flag
-        token.removeChild(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.removeChild(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Finish relaunching and ensure flag is now not reported
-        token.finishRelaunching();
-        assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+        mToken.finishRelaunching();
+        assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
deleted file mode 100644
index c3a471a..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.wm;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DimLayerController} class.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
- */
-@SmallTest
-@Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
-public class DimLayerControllerTests extends WindowTestsBase {
-
-    /**
-     * This tests if shared fullscreen dim layer is added when stack is added to display
-     * and is removed when the only stack on the display is removed.
-     */
-    @Test
-    public void testSharedFullScreenDimLayer() throws Exception {
-        // Create a display.
-        final DisplayContent dc = createNewDisplay();
-        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
-        // Add stack with activity.
-        final TaskStack stack = createTaskStackOnDisplay(dc);
-        assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
-        assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
-        // Remove the only stack on the display and check if the shared dim layer clears.
-        stack.removeImmediately();
-        assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
-        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
new file mode 100644
index 0000000..f069d49
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -0,0 +1,207 @@
+/*
+ * 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.wm;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+/**
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DimmerTests extends WindowTestsBase {
+    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+        final SurfaceControl mControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+
+        @Override
+        SurfaceControl getSurfaceControl() {
+            return mControl;
+        }
+
+        @Override
+        SurfaceControl.Transaction getPendingTransaction() {
+            return mTransaction;
+        }
+    }
+
+    private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+        final SurfaceSession mSession = new SurfaceSession();
+        SurfaceControl mBuiltSurface = null;
+        final SurfaceControl mHostControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+
+        class MockSurfaceBuilder extends SurfaceControl.Builder {
+            MockSurfaceBuilder(SurfaceSession ss) {
+                super(ss);
+            }
+
+            @Override
+            public SurfaceControl build() {
+                SurfaceControl sc = mock(SurfaceControl.class);
+                mBuiltSurface = sc;
+                return sc;
+            }
+        }
+
+        @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            return new MockSurfaceBuilder(mSession);
+        }
+
+        @Override
+        SurfaceControl getSurfaceControl() {
+            return mHostControl;
+        }
+
+        @Override
+        SurfaceControl.Transaction getPendingTransaction() {
+            return mHostTransaction;
+        }
+    }
+
+    MockSurfaceBuildingContainer mHost;
+    Dimmer mDimmer;
+    SurfaceControl.Transaction mTransaction;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mHost = new MockSurfaceBuildingContainer();
+        mTransaction = mock(SurfaceControl.Transaction.class);
+        mDimmer = new Dimmer(mHost);
+    }
+
+    @Test
+    public void testDimAboveNoChildCreatesSurface() throws Exception {
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+        float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, alpha);
+        final SurfaceControl firstSurface = mHost.mBuiltSurface;
+
+        alpha = 0.9f;
+        mDimmer.dimAbove(mTransaction, alpha);
+
+        assertEquals(firstSurface, mHost.mBuiltSurface);
+        verify(mTransaction).setAlpha(firstSurface, 0.9f);
+    }
+
+    @Test
+    public void testUpdateDimsAppliesSize() throws Exception {
+        mDimmer.dimAbove(mTransaction, 0.8f);
+
+        int width = 100;
+        int height = 300;
+        Rect bounds = new Rect(0, 0, width, height);
+        mDimmer.updateDims(mTransaction, bounds);
+        verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+    }
+
+    @Test
+    public void testDimAboveNoChildNotReset() throws Exception {
+        mDimmer.dimAbove(mTransaction, 0.8f);
+        mDimmer.resetDimStates();
+
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface, never()).destroy();
+    }
+
+    @Test
+    public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimBelow(mTransaction, child, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        mDimmer.resetDimStates();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface).destroy();
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        mDimmer.resetDimStates();
+        mDimmer.dimAbove(mTransaction, child, alpha);
+
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface, never()).destroy();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def7..873a01b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
         mPositioner = new TaskPositioner(sWm);
-        mPositioner.register(display);
+        mPositioner.register(mDisplayContent);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index b846fd0..0ef78f4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -105,14 +105,10 @@
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         assertEquals(stack, task.mStack);
-        assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
-        assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
 
         // Remove stack and check if its child is also removed.
         stack.removeImmediately();
         assertNull(stack.getDisplayContent());
         assertNull(task.mStack);
-        assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
-        assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 53d0bfb..a45695f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -568,11 +568,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        return false;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         return false;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +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.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
-        sWm.mInputMethodTarget = null;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The Ime has an higher base layer than app windows and lower base layer than system
-        // windows, so it should be above app windows and below system windows if there isn't an IME
-        // target.
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows and below system windows if it is targeting an app
-        // window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
-                "imeAppTargetChildAboveWindow");
-        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
-                "imeAppTargetChildBelowWindow");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for child windows that are z-ordered above it
-        // and below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
-        final WindowState appBelowImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState appAboveImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for non-fullscreen app window above it and
-        // below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
-        assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
-        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
-                mDisplayContent, "imeSystemOverlayTarget",
-                true /* ownerCanAddInternalSystemWindow */);
-
-        sWm.mInputMethodTarget = imeSystemOverlayTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The IME target base layer is higher than all window except for the nav bar window, so the
-        // IME should be above all windows except for the nav bar.
-        assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testStackLayers() throws Exception {
-        WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
-        WindowState dockedStackWindow = createWindowOnStack(null,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
-                mDisplayContent, "dockedStackWindow");
-        WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
-                mDisplayContent, "assistantStackWindow");
-
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
-        assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
-        assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
-    }
-
-    private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
-            throws Exception {
-        assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
-    }
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0980f7e..4c5e291 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@
     private static boolean sOneTimeSetupDone = false;
     DisplayContent mDisplayContent;
     DisplayInfo mDisplayInfo = new DisplayInfo();
-    WindowLayersController mLayersController;
     WindowState mWallpaperWindow;
     WindowState mImeWindow;
     WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@
 
         final Context context = InstrumentationRegistry.getTargetContext();
         AttributeCache.init(context);
+
         sWm = TestWindowManagerPolicy.getWindowManagerService(context);
-        mLayersController = new WindowLayersController(sWm);
+        beforeCreateDisplay();
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
         mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@
         waitUntilHandlersIdle();
     }
 
+    void beforeCreateDisplay() {
+        // Called before display is created.
+    }
+
     @After
     public void tearDown() throws Exception {
         final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@
         waitUntilHandlersIdle();
     }
 
+    /**
+     * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+     *         set-up (or null).
+     */
+    SurfaceBuilderFactory getSurfaceBuilderFactory() {
+        return null;
+    }
+
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
         final WindowState win = createWindow(parent, type, name);
         mCommonWindows.add(win);
@@ -162,6 +174,11 @@
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
     }
 
+    /** Asserts that the first entry is greater than the second entry. */
+    void assertLessThan(int first, int second) throws Exception {
+        Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+    }
+
     /**
      * Waits until the main handler for WM has processed all messages.
      */
@@ -264,7 +281,7 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+        return new DisplayContent(display, sWm, new WallpaperController(sWm));
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 692e08b..7219104 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@
         assertEquals(null, dc.getWindowToken(token.token));
     }
 
-    @Test
-    public void testAdjustAnimLayer() throws Exception {
-        final WindowTestUtils.TestWindowToken token =
-                new WindowTestUtils.TestWindowToken(0, mDisplayContent);
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
-        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
-        window2.mLayer = 100;
-        window3.mLayer = 200;
-
-        // We assign layers once, to get the base values computed by
-        // the controller.
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
-        final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
-        final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
-        final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
-        final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
-        // Then we set an adjustment, and assign them again, they should
-        // be offset.
-        int adj = token.adj = 50;
-        mLayersController.assignWindowLayers(mDisplayContent);
-        final int highestLayer = token.getHighestAnimLayer();
-
-        assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
-        assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
-        assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
-        assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, highestLayer);
-    }
-
     /**
      * Test that a window token isn't orphaned by the system when it is requested to be removed.
      * Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 0000000..f7c4b1f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * 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.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+        HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+        HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+        @Override
+        public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+            mRelativeLayersForControl.remove(sc);
+            mLayersForControl.put(sc, layer);
+            return super.setLayer(sc, layer);
+        }
+
+        @Override
+        public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+                SurfaceControl relativeTo,
+                int layer) {
+            mRelativeLayersForControl.put(sc, relativeTo);
+            mLayersForControl.put(sc, layer);
+            return super.setRelativeLayer(sc, relativeTo, layer);
+        }
+
+        int getLayer(SurfaceControl sc) {
+            return mLayersForControl.get(sc);
+        }
+
+        SurfaceControl getRelativeLayer(SurfaceControl sc) {
+            return mRelativeLayersForControl.get(sc);
+        }
+    };
+
+    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+    // such that we can keep track of the parents of Surfaces as they are constructed.
+    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+    private class HierarchyRecorder extends SurfaceControl.Builder {
+        SurfaceControl mPendingParent;
+
+        HierarchyRecorder(SurfaceSession s) {
+            super(s);
+        }
+
+        public SurfaceControl.Builder setParent(SurfaceControl sc) {
+            mPendingParent = sc;
+            return super.setParent(sc);
+        }
+        public SurfaceControl build() {
+            SurfaceControl sc = super.build();
+            mParentFor.put(sc, mPendingParent);
+            mPendingParent = null;
+            return sc;
+        }
+    };
+
+    class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new HierarchyRecorder(s);
+        }
+    };
+
+    private LayerRecordingTransaction mTransaction;
+
+    @Override
+    void beforeCreateDisplay() {
+        // We can't use @Before here because it may happen after WindowTestsBase @Before
+        // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+        // would miss construction of the top-level layers.
+        mTransaction = new LayerRecordingTransaction();
+        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+    }
+
+    @After
+    public void after() {
+        mTransaction.close();
+        mParentFor.clear();
+    }
+
+    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+        LinkedList<SurfaceControl> p = new LinkedList();
+        SurfaceControl current = sc;
+        do {
+            p.addLast(current);
+
+            SurfaceControl rs = t.getRelativeLayer(current);
+            if (rs != null) {
+                current = rs;
+            } else {
+                current = mParentFor.get(current);
+            }
+        } while (current != null);
+        return p;
+    }
+
+    void assertZOrderGreaterThan(LayerRecordingTransaction t,
+            SurfaceControl left, SurfaceControl right) throws Exception {
+        final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+        final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+        SurfaceControl commonAncestor = null;
+        SurfaceControl leftTop = leftParentChain.peekLast();
+        SurfaceControl rightTop = rightParentChain.peekLast();
+        while (leftTop != null && rightTop != null && leftTop == rightTop) {
+            commonAncestor = leftParentChain.removeLast();
+            rightParentChain.removeLast();
+            leftTop = leftParentChain.peekLast();
+            rightTop = rightParentChain.peekLast();
+        }
+
+        if (rightTop == null) { // right is the parent of left.
+            assertGreaterThan(t.getLayer(leftTop), 0);
+        } else if (leftTop == null) { // left is the parent of right.
+            assertGreaterThan(0, t.getLayer(rightTop));
+        } else {
+            assertGreaterThan(t.getLayer(leftTop),
+                    t.getLayer(rightTop));
+        }
+    }
+
+    void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+            WindowState left, WindowState right) throws Exception {
+        assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+        sWm.mInputMethodTarget = null;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The Ime has an higher base layer than app windows and lower base layer than system
+        // windows, so it should be above app windows and below system windows if there isn't an IME
+        // target.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows and below system windows if it is targeting an app
+        // window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+                "imeAppTargetChildAboveWindow");
+        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+                "imeAppTargetChildBelowWindow");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for child windows that are z-ordered above it
+        // and below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+        final WindowState appBelowImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState appAboveImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for non-fullscreen app window above it and
+        // below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+        assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+                mDisplayContent, "imeSystemOverlayTarget",
+                true /* ownerCanAddInternalSystemWindow */);
+
+        sWm.mInputMethodTarget = imeSystemOverlayTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The IME target base layer is higher than all window except for the nav bar window, so the
+        // IME should be above all windows except for the nav bar.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+        // The IME has a higher base layer than the status bar so we may expect it to go
+        // above the status bar once they are both in the Non-App layer, as past versions of this
+        // test enforced. However this seems like the wrong behavior unless the status bar is the
+        // IME target.
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+        sWm.mInputMethodTarget = mStatusBarWindow;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testStackLayers() throws Exception {
+        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+                "pinnedStackWindow");
+        final WindowState dockedStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                mDisplayContent, "dockedStackWindow");
+        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+                mDisplayContent, "assistantStackWindow");
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 1e5eb05..7ca17af 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -91,8 +91,6 @@
     private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
     private long mScreenOnDuration; // Total screen on duration since device was "born"
 
-    private long mElapsedTimeThreshold;
-    private long mScreenOnTimeThreshold;
     private final File mStorageDir;
 
     private boolean mScreenOn;
@@ -113,11 +111,6 @@
         readScreenOnTime();
     }
 
-    public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
-        mElapsedTimeThreshold = elapsedTimeThreshold;
-        mScreenOnTimeThreshold = screenOnTimeThreshold;
-    }
-
     public void updateDisplay(boolean screenOn, long elapsedRealtime) {
         if (screenOn == mScreenOn) return;
 
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 98a2e28..cd0fce6 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -140,9 +140,7 @@
     static final int MSG_PAROLE_STATE_CHANGED = 9;
     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
 
-    long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
-    long mAppIdleWallclockThresholdMillis;
     long mAppIdleParoleIntervalMillis;
     long mAppIdleParoleDurationMillis;
     long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
@@ -912,14 +910,6 @@
         pw.println();
         pw.println("Settings:");
 
-        pw.print("  mAppIdleDurationMillis=");
-        TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
-        pw.println();
-
-        pw.print("  mAppIdleWallclockThresholdMillis=");
-        TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
-        pw.println();
-
         pw.print("  mCheckIdleIntervalMillis=");
         TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
         pw.println();
@@ -1192,24 +1182,12 @@
                     // fallthrough, mParser is empty and all defaults will be returned.
                 }
 
-                // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
-                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
-                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
-                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
-                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
-                        COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
                 // Default: 24 hours between paroles
                 mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
                         COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
 
                 mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
-                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
-                        mAppIdleScreenThresholdMillis);
 
                 String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
                 mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
@@ -1219,6 +1197,9 @@
                         null);
                 mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
                         ELAPSED_TIME_THRESHOLDS);
+                mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+                        COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
+
             }
         }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 64cc7c1..1e6abf2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -362,6 +362,9 @@
 
     /**
      * TelephonyProvider column name for enable Volte.
+     *
+     * If this setting is not initialized (set to -1)  then we use the Carrier Config value
+     * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
      *@hide
      */
     public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index c9f3199..fd42033 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -72,6 +72,9 @@
 
         self.ident = self.raw.replace(" deprecated ", " ")
 
+    def __hash__(self):
+        return hash(self.raw)
+
     def __repr__(self):
         return self.raw
 
@@ -110,6 +113,9 @@
             ident = ident[:ident.index(" throws ")]
         self.ident = ident
 
+    def __hash__(self):
+        return hash(self.raw)
+
     def __repr__(self):
         return self.raw
 
@@ -145,6 +151,9 @@
 
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
+    def __hash__(self):
+        return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
+
     def __repr__(self):
         return self.raw
 
@@ -256,6 +265,14 @@
     _fail(clazz, detail, True, rule, msg)
 
 
+noticed = {}
+
+def notice(clazz):
+    global noticed
+
+    noticed[clazz.fullname] = hash(clazz)
+
+
 def verify_constants(clazz):
     """All static final constants must be FOO_NAME style."""
     if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1203,6 +1220,9 @@
 
 def examine_clazz(clazz):
     """Find all style issues in the given class."""
+
+    notice(clazz)
+
     if clazz.pkg.name.startswith("java"): return
     if clazz.pkg.name.startswith("junit"): return
     if clazz.pkg.name.startswith("org.apache"): return
@@ -1258,10 +1278,11 @@
 
 def examine_stream(stream):
     """Find all style issues in the given API stream."""
-    global failures
+    global failures, noticed
     failures = {}
+    noticed = {}
     _parse_stream(stream, examine_clazz)
-    return failures
+    return (failures, noticed)
 
 
 def examine_api(api):
@@ -1338,6 +1359,8 @@
             help="Disable terminal colors")
     parser.add_argument("--allow-google", action='store_const', const=True,
             help="Allow references to Google")
+    parser.add_argument("--show-noticed", action='store_const', const=True,
+            help="Show API changes noticed")
     args = vars(parser.parse_args())
 
     if args['no_color']:
@@ -1350,16 +1373,21 @@
     previous_file = args['previous.txt']
 
     with current_file as f:
-        cur_fail = examine_stream(f)
+        cur_fail, cur_noticed = examine_stream(f)
     if not previous_file is None:
         with previous_file as f:
-            prev_fail = examine_stream(f)
+            prev_fail, prev_noticed = examine_stream(f)
 
         # ignore errors from previous API level
         for p in prev_fail:
             if p in cur_fail:
                 del cur_fail[p]
 
+        # ignore classes unchanged from previous API level
+        for k, v in prev_noticed.iteritems():
+            if k in cur_noticed and v == cur_noticed[k]:
+                del cur_noticed[k]
+
         """
         # NOTE: disabled because of memory pressure
         # look for compatibility issues
@@ -1371,6 +1399,12 @@
             print
         """
 
+    if args['show_noticed'] and len(cur_noticed) != 0:
+        print "%s API changes noticed %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+        for f in sorted(cur_noticed.keys()):
+            print f
+        print
+
     if len(cur_fail) != 0:
         print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
         for f in sorted(cur_fail):
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index dcb90e4..15d39fd 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -163,11 +163,14 @@
             'U+%04X was not found in %s' % (char, font))
 
 
-def assert_font_supports_none_of_chars(font, chars):
+def assert_font_supports_none_of_chars(font, chars, fallbackName):
     best_cmap = get_best_cmap(font)
     for char in chars:
-        assert char not in best_cmap, (
-            'U+%04X was found in %s' % (char, font))
+        if fallbackName:
+            assert char not in best_cmap, 'U+%04X was found in %s' % (char, font)
+        else:
+            assert char not in best_cmap, (
+                'U+%04X was found in %s in fallback %s' % (char, font, fallbackName))
 
 
 def assert_font_supports_all_sequences(font, sequences):
@@ -196,19 +199,21 @@
 
 
 class FontRecord(object):
-    def __init__(self, name, scripts, variant, weight, style, font):
+    def __init__(self, name, scripts, variant, weight, style, fallback_for, font):
         self.name = name
         self.scripts = scripts
         self.variant = variant
         self.weight = weight
         self.style = style
+        self.fallback_for = fallback_for
         self.font = font
 
 
 def parse_fonts_xml(fonts_xml_path):
-    global _script_to_font_map, _fallback_chain
+    global _script_to_font_map, _fallback_chains, _all_fonts
     _script_to_font_map = collections.defaultdict(set)
-    _fallback_chain = []
+    _fallback_chains = {}
+    _all_fonts = []
     tree = ElementTree.parse(fonts_xml_path)
     families = tree.findall('family')
     # Minikin supports up to 254 but users can place their own font at the first
@@ -225,10 +230,17 @@
                 'No variant expected for LGC font %s.' % name)
             assert langs is None, (
                 'No language expected for LGC fonts %s.' % name)
+            assert name not in _fallback_chains, 'Duplicated name entry %s' % name
+            _fallback_chains[name] = []
         else:
             assert variant in {None, 'elegant', 'compact'}, (
                 'Unexpected value for variant: %s' % variant)
 
+    for family in families:
+        name = family.get('name')
+        variant = family.get('variant')
+        langs = family.get('lang')
+
         if langs:
             langs = langs.split()
             scripts = {lang_to_script(lang) for lang in langs}
@@ -247,17 +259,36 @@
             assert style in {'normal', 'italic'}, (
                 'Unknown style "%s"' % style)
 
+            fallback_for = child.get('fallbackFor')
+
+            assert not name or not fallback_for, (
+                'name and fallbackFor cannot be present at the same time')
+            assert not fallback_for or fallback_for in _fallback_chains, (
+                'Unknown fallback name: %s' % fallback_for)
+
             index = child.get('index')
             if index:
                 index = int(index)
 
-            _fallback_chain.append(FontRecord(
+            record = FontRecord(
                 name,
                 frozenset(scripts),
                 variant,
                 weight,
                 style,
-                (font_file, index)))
+                fallback_for,
+                (font_file, index))
+
+            _all_fonts.append(record)
+
+            if not fallback_for:
+                if not name or name == 'sans-serif':
+                    for _, fallback in _fallback_chains.iteritems():
+                        fallback.append(record)
+                else:
+                    _fallback_chains[name].append(record)
+            else:
+                _fallback_chains[fallback_for].append(record)
 
             if name: # non-empty names are used for default LGC fonts
                 map_scripts = {'Latn', 'Grek', 'Cyrl'}
@@ -274,7 +305,7 @@
 
 def get_emoji_font():
     emoji_fonts = [
-        record.font for record in _fallback_chain
+        record.font for record in _all_fonts
         if 'Zsye' in record.scripts]
     assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
     return emoji_fonts[0]
@@ -318,35 +349,36 @@
 
 def check_emoji_defaults(default_emoji):
     missing_text_chars = _emoji_properties['Emoji'] - default_emoji
-    emoji_font_seen = False
-    for record in _fallback_chain:
-        if 'Zsye' in record.scripts:
-            emoji_font_seen = True
-            # No need to check the emoji font
-            continue
-        # For later fonts, we only check them if they have a script
-        # defined, since the defined script may get them to a higher
-        # score even if they appear after the emoji font. However,
-        # we should skip checking the text symbols font, since
-        # symbol fonts should be able to override the emoji display
-        # style when 'Zsym' is explicitly specified by the user.
-        if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
-            continue
+    for name, fallback_chain in _fallback_chains.iteritems():
+        emoji_font_seen = False
+        for record in fallback_chain:
+            if 'Zsye' in record.scripts:
+                emoji_font_seen = True
+                # No need to check the emoji font
+                continue
+            # For later fonts, we only check them if they have a script
+            # defined, since the defined script may get them to a higher
+            # score even if they appear after the emoji font. However,
+            # we should skip checking the text symbols font, since
+            # symbol fonts should be able to override the emoji display
+            # style when 'Zsym' is explicitly specified by the user.
+            if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
+                continue
 
-        # Check default emoji-style characters
-        assert_font_supports_none_of_chars(record.font, sorted(default_emoji))
+            # Check default emoji-style characters
+            assert_font_supports_none_of_chars(record.font, sorted(default_emoji), name)
 
-        # Mark default text-style characters appearing in fonts above the emoji
-        # font as seen
-        if not emoji_font_seen:
-            missing_text_chars -= set(get_best_cmap(record.font))
+            # Mark default text-style characters appearing in fonts above the emoji
+            # font as seen
+            if not emoji_font_seen:
+                missing_text_chars -= set(get_best_cmap(record.font))
 
-    # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
-    # webdings yet.
-    missing_text_chars -= _chars_by_age['7.0']
-    assert missing_text_chars == set(), (
-        'Text style version of some emoji characters are missing: ' +
-            repr(missing_text_chars))
+        # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
+        # webdings yet.
+        missing_text_chars -= _chars_by_age['7.0']
+        assert missing_text_chars == set(), (
+            'Text style version of some emoji characters are missing: ' +
+                repr(missing_text_chars))
 
 
 # Setting reverse to true returns a dictionary that maps the values to sets of
@@ -626,8 +658,19 @@
     return all_emoji, default_emoji, equivalent_emoji
 
 
+def check_compact_only_fallback():
+    for name, fallback_chain in _fallback_chains.iteritems():
+        for record in fallback_chain:
+            if record.variant == 'compact':
+                same_script_elegants = [x for x in fallback_chain
+                    if x.scripts == record.scripts and x.variant == 'elegant']
+                assert same_script_elegants, (
+                    '%s must be in elegant of %s as fallback of "%s" too' % (
+                    record.font, record.scripts, record.fallback_for),)
+
+
 def check_vertical_metrics():
-    for record in _fallback_chain:
+    for record in _all_fonts:
         if record.name in ['sans-serif', 'sans-serif-condensed']:
             font = open_font(record.font)
             assert font['head'].yMax == 2163 and font['head'].yMin == -555, (
@@ -646,11 +689,12 @@
 def check_cjk_punctuation():
     cjk_scripts = {'Hans', 'Hant', 'Jpan', 'Kore'}
     cjk_punctuation = range(0x3000, 0x301F + 1)
-    for record in _fallback_chain:
-        if record.scripts.intersection(cjk_scripts):
-            # CJK font seen. Stop checking the rest of the fonts.
-            break
-        assert_font_supports_none_of_chars(record.font, cjk_punctuation)
+    for name, fallback_chain in _fallback_chains.iteritems():
+        for record in fallback_chain:
+            if record.scripts.intersection(cjk_scripts):
+                # CJK font seen. Stop checking the rest of the fonts.
+                break
+            assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
 
 
 def main():
@@ -661,6 +705,8 @@
     fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
     parse_fonts_xml(fonts_xml_path)
 
+    check_compact_only_fallback()
+
     check_vertical_metrics()
 
     hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
diff --git a/vr/Android.mk b/vr/Android.mk
index 5b65d3f..73e9f23 100644
--- a/vr/Android.mk
+++ b/vr/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_MODULE := libdvr_loader
 LOCAL_MODULE_OWNER := google
 LOCAL_SRC_FILES := dvr_library_loader.cpp
+LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_SHARED_LIBRARY)
 
 # Java platform library for vr stuff.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index c2f11d9..a9e1e9d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -66,11 +66,11 @@
 
     List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
 
-    int addOrUpdateNetwork(in WifiConfiguration config, String packageName);
+    int addOrUpdateNetwork(in WifiConfiguration config);
 
-    boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config, String packageName);
+    boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config);
 
-    boolean removePasspointConfiguration(in String fqdn, String packageName);
+    boolean removePasspointConfiguration(in String fqdn);
 
     List<PasspointConfiguration> getPasspointConfigurations();
 
@@ -80,21 +80,21 @@
 
     void deauthenticateNetwork(long holdoff, boolean ess);
 
-    boolean removeNetwork(int netId, String packageName);
+    boolean removeNetwork(int netId);
 
-    boolean enableNetwork(int netId, boolean disableOthers, String packageName);
+    boolean enableNetwork(int netId, boolean disableOthers);
 
-    boolean disableNetwork(int netId, String packageName);
+    boolean disableNetwork(int netId);
 
-    void startScan(in ScanSettings requested, in WorkSource ws, String packageName);
+    void startScan(in ScanSettings requested, in WorkSource ws, in String packageName);
 
     List<ScanResult> getScanResults(String callingPackage);
 
-    void disconnect(String packageName);
+    void disconnect();
 
-    void reconnect(String packageName);
+    void reconnect();
 
-    void reassociate(String packageName);
+    void reassociate();
 
     WifiInfo getConnectionInfo(String callingPackage);
 
@@ -108,7 +108,7 @@
 
     boolean isDualBandSupported();
 
-    boolean saveConfiguration(String packageName);
+    boolean saveConfiguration();
 
     DhcpInfo getDhcpInfo();
 
@@ -134,9 +134,9 @@
 
     boolean stopSoftAp();
 
-    int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, String packageName);
+    int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, in String packageName);
 
-    void stopLocalOnlyHotspot(String packageName);
+    void stopLocalOnlyHotspot();
 
     void startWatchLocalOnlyHotspot(in Messenger messenger, in IBinder binder);
 
@@ -146,9 +146,9 @@
 
     WifiConfiguration getWifiApConfiguration();
 
-    void setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
+    void setWifiApConfiguration(in WifiConfiguration wifiConfig);
 
-    Messenger getWifiServiceMessenger(String packageName);
+    Messenger getWifiServiceMessenger();
 
     void enableTdls(String remoteIPAddress, boolean enable);
 
@@ -166,16 +166,16 @@
     void setAllowScansWithTraffic(int enabled);
     int getAllowScansWithTraffic();
 
-    boolean setEnableAutoJoinWhenAssociated(boolean enabled, String packageName);
+    boolean setEnableAutoJoinWhenAssociated(boolean enabled);
     boolean getEnableAutoJoinWhenAssociated();
 
     void enableWifiConnectivityManager(boolean enabled);
 
     WifiConnectionStatistics getConnectionStatistics();
 
-    void disableEphemeralNetwork(String SSID, String packageName);
+    void disableEphemeralNetwork(String SSID);
 
-    void factoryReset(String packageName);
+    void factoryReset();
 
     Network getCurrentNetwork();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 66fabf3..183cf0d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1128,7 +1128,7 @@
      */
     private int addOrUpdateNetwork(WifiConfiguration config) {
         try {
-            return mService.addOrUpdateNetwork(config, mContext.getOpPackageName());
+            return mService.addOrUpdateNetwork(config);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1149,7 +1149,7 @@
      */
     public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            if (!mService.addOrUpdatePasspointConfiguration(config, mContext.getOpPackageName())) {
+            if (!mService.addOrUpdatePasspointConfiguration(config)) {
                 throw new IllegalArgumentException();
             }
         } catch (RemoteException e) {
@@ -1166,7 +1166,7 @@
      */
     public void removePasspointConfiguration(String fqdn) {
         try {
-            if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) {
+            if (!mService.removePasspointConfiguration(fqdn)) {
                 throw new IllegalArgumentException();
             }
         } catch (RemoteException e) {
@@ -1252,7 +1252,7 @@
      */
     public boolean removeNetwork(int netId) {
         try {
-            return mService.removeNetwork(netId, mContext.getOpPackageName());
+            return mService.removeNetwork(netId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1298,7 +1298,7 @@
 
         boolean success;
         try {
-            success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
+            success = mService.enableNetwork(netId, attemptConnect);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1324,7 +1324,7 @@
      */
     public boolean disableNetwork(int netId) {
         try {
-            return mService.disableNetwork(netId, mContext.getOpPackageName());
+            return mService.disableNetwork(netId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1337,7 +1337,7 @@
      */
     public boolean disconnect() {
         try {
-            mService.disconnect(mContext.getOpPackageName());
+            mService.disconnect();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1352,7 +1352,7 @@
      */
     public boolean reconnect() {
         try {
-            mService.reconnect(mContext.getOpPackageName());
+            mService.reconnect();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1367,7 +1367,7 @@
      */
     public boolean reassociate() {
         try {
-            mService.reassociate(mContext.getOpPackageName());
+            mService.reassociate();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1740,7 +1740,7 @@
     @Deprecated
     public boolean saveConfiguration() {
         try {
-            return mService.saveConfiguration(mContext.getOpPackageName());
+            return mService.saveConfiguration();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2056,7 +2056,7 @@
             }
             mLOHSCallbackProxy = null;
             try {
-                mService.stopLocalOnlyHotspot(mContext.getOpPackageName());
+                mService.stopLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2175,7 +2175,7 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+            mService.setWifiApConfiguration(wifiConfig);
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2947,7 +2947,7 @@
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
-            mService.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
+            mService.disableEphemeralNetwork(SSID);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2986,7 +2986,7 @@
      */
     public Messenger getWifiServiceMessenger() {
         try {
-            return mService.getWifiServiceMessenger(mContext.getOpPackageName());
+            return mService.getWifiServiceMessenger();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3516,7 +3516,7 @@
      */
     public void factoryReset() {
         try {
-            mService.factoryReset(mContext.getOpPackageName());
+            mService.factoryReset();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3543,7 +3543,7 @@
      */
     public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
         try {
-            return mService.setEnableAutoJoinWhenAssociated(enabled, mContext.getOpPackageName());
+            return mService.setEnableAutoJoinWhenAssociated(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 3cad590..0df5615 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -137,7 +137,7 @@
 
         assertEquals(mApConfig, callback.mRes.getWifiConfiguration());
         callback.mRes.close();
-        verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
+        verify(mWifiService).stopLocalOnlyHotspot();
     }
 
     /**
@@ -157,7 +157,7 @@
             assertEquals(mApConfig, res.getWifiConfiguration());
         }
 
-        verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
+        verify(mWifiService).stopLocalOnlyHotspot();
     }
 
     /**
@@ -548,7 +548,7 @@
                 anyString())).thenReturn(REQUEST_REGISTERED);
         mWifiManager.startLocalOnlyHotspot(callback, mHandler);
         mWifiManager.cancelLocalOnlyHotspotRequest();
-        verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
+        verify(mWifiService).stopLocalOnlyHotspot();
     }
 
     /**
@@ -570,7 +570,7 @@
                 anyString())).thenReturn(REQUEST_REGISTERED);
         mWifiManager.startLocalOnlyHotspot(callback, mHandler);
         mWifiManager.cancelLocalOnlyHotspotRequest();
-        verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
+        verify(mWifiService).stopLocalOnlyHotspot();
         mLooper.dispatchAll();
         assertEquals(ERROR_NOT_SET, callback.mFailureReason);
         assertFalse(callback.mOnStartedCalled);
@@ -594,7 +594,7 @@
         assertFalse(callback.mOnStoppedCalled);
         assertEquals(null, callback.mRes);
         mWifiManager.cancelLocalOnlyHotspotRequest();
-        verify(mWifiService, never()).stopLocalOnlyHotspot(anyString());
+        verify(mWifiService, never()).stopLocalOnlyHotspot();
     }
 
     /**