Merge "Fix a flaky test" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 3f06ffd..b0e0b35 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1133,16 +1133,6 @@
     output: "framework-aidl-mappings.txt",
 }
 
-genrule {
-    name: "framework-annotation-proc-index",
-    srcs: [":framework-annotation-proc"],
-    cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
-    out: ["unsupportedappusage_index.csv"],
-    dist: {
-        targets: ["droidcore"],
-    },
-}
-
 // Avoid including Parcelable classes as we don't want to have two copies of
 // Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
 // and TeleService app (packages/services/Telephony).
diff --git a/apex/Android.bp b/apex/Android.bp
index 88c43f9..cd34f98 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -60,6 +60,10 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/public/api",
+    },
 }
 
 stubs_defaults {
@@ -74,6 +78,10 @@
             removed_api_file: "api/system-removed.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system/api",
+    },
 }
 
 // The defaults for module_libs comes in two parts - defaults for API checks
@@ -93,6 +101,10 @@
             removed_api_file: "api/module-lib-removed.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/module-lib/api",
+    },
 }
 
 stubs_defaults {
@@ -113,6 +125,10 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system-server/api",
+    },
 }
 
 // Empty for now, but a convenient place to add rules for all
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index cb87c6c..3f254c0 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -563,12 +563,10 @@
 
     /**
      * Return the {@link BlobHandle BlobHandles} corresponding to the data blobs that
-     * the calling app has acquired a lease on using {@link #acquireLease(BlobHandle, int)} or
-     * one of it's other variants.
+     * the calling app currently has a lease on.
      *
-     * @hide
+     * @return a list of {@link BlobHandle BlobHandles} that the caller has a lease on.
      */
-    @TestApi
     @NonNull
     public List<BlobHandle> getLeasedBlobs() throws IOException {
         try {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 02c55b7..5f86ed6 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -52,6 +52,7 @@
 import com.google.android.exoplayer2.upstream.DataSpec;
 import com.google.android.exoplayer2.upstream.TransferListener;
 import com.google.android.exoplayer2.util.ParsableByteArray;
+import com.google.android.exoplayer2.util.Util;
 import com.google.android.exoplayer2.video.ColorInfo;
 
 import java.io.EOFException;
@@ -60,6 +61,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -688,12 +690,83 @@
      * Returns an immutable list with the names of the parsers that are suitable for container
      * formats with the given {@link MediaFormat}.
      *
-     * <p>TODO: List which properties are taken into account. E.g. MimeType.
+     * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
+     * MediaFormat#KEY_MIME} corresponds to the supported container format.
+     *
+     * @param mediaFormat The {@link MediaFormat} to check support for.
+     * @return The parser names that support the given {@code mediaFormat}, or the list of all
+     *     parsers available if no container specific format information is provided.
      */
     @NonNull
     @ParserName
     public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
-        throw new UnsupportedOperationException();
+        String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+        mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
+        if (TextUtils.isEmpty(mimeType)) {
+            // No MIME type provided. Return all.
+            return Collections.unmodifiableList(
+                    new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
+        }
+        ArrayList<String> result = new ArrayList<>();
+        switch (mimeType) {
+            case "video/x-matroska":
+            case "audio/x-matroska":
+            case "video/x-webm":
+            case "audio/x-webm":
+                result.add(PARSER_NAME_MATROSKA);
+                break;
+            case "video/mp4":
+            case "audio/mp4":
+            case "application/mp4":
+                result.add(PARSER_NAME_MP4);
+                result.add(PARSER_NAME_FMP4);
+                break;
+            case "audio/mpeg":
+                result.add(PARSER_NAME_MP3);
+                break;
+            case "audio/aac":
+                result.add(PARSER_NAME_ADTS);
+                break;
+            case "audio/ac3":
+                result.add(PARSER_NAME_AC3);
+                break;
+            case "video/mp2t":
+            case "audio/mp2t":
+                result.add(PARSER_NAME_TS);
+                break;
+            case "video/x-flv":
+                result.add(PARSER_NAME_FLV);
+                break;
+            case "video/ogg":
+            case "audio/ogg":
+            case "application/ogg":
+                result.add(PARSER_NAME_OGG);
+                break;
+            case "video/mp2p":
+            case "video/mp1s":
+                result.add(PARSER_NAME_PS);
+                break;
+            case "audio/vnd.wave":
+            case "audio/wav":
+            case "audio/wave":
+            case "audio/x-wav":
+                result.add(PARSER_NAME_WAV);
+                break;
+            case "audio/amr":
+                result.add(PARSER_NAME_AMR);
+                break;
+            case "audio/ac4":
+                result.add(PARSER_NAME_AC4);
+                break;
+            case "audio/flac":
+            case "audio/x-flac":
+                result.add(PARSER_NAME_FLAC);
+                break;
+            default:
+                // No parsers support the given mime type. Do nothing.
+                break;
+        }
+        return Collections.unmodifiableList(result);
     }
 
     // Private fields.
diff --git a/apex/statsd/framework/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
index 1a45c4a..8bd36a5 100644
--- a/apex/statsd/framework/java/android/util/StatsEvent.java
+++ b/apex/statsd/framework/java/android/util/StatsEvent.java
@@ -188,6 +188,12 @@
     @VisibleForTesting
     public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
 
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000;
+
     // Size limits.
 
     /**
@@ -350,19 +356,32 @@
             mPos = 0;
             writeTypeId(TYPE_OBJECT);
 
-            // Set mPos to after atom id's location in the buffer.
-            // First 2 elements in the buffer are event timestamp followed by the atom id.
-            mPos = POS_ATOM_ID + Byte.BYTES + Integer.BYTES;
-            mPosLastField = 0;
-            mLastType = 0;
+            // Write timestamp.
+            mPos = POS_TIMESTAMP_NS;
+            writeLong(mTimestampNs);
         }
 
         /**
          * Sets the atom id for this StatsEvent.
+         *
+         * This should be called immediately after StatsEvent.newBuilder()
+         * and should only be called once.
+         * Not calling setAtomId will result in ERROR_NO_ATOM_ID.
+         * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION.
          **/
         @NonNull
         public Builder setAtomId(final int atomId) {
-            mAtomId = atomId;
+            if (0 == mAtomId) {
+                mAtomId = atomId;
+
+                if (1 == mNumElements) { // Only timestamp is written so far.
+                    writeInt(atomId);
+                } else {
+                    // setAtomId called out of order.
+                    mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION;
+                }
+            }
+
             return this;
         }
 
@@ -557,7 +576,7 @@
         public Builder addBooleanAnnotation(
                 final byte annotationId, final boolean value) {
             // Ensure there's a field written to annotate.
-            if (0 == mPosLastField) {
+            if (mNumElements < 2) {
                 mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
             } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
                 mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
@@ -568,6 +587,7 @@
                 mCurrentAnnotationCount++;
                 writeAnnotationCount();
             }
+
             return this;
         }
 
@@ -576,7 +596,7 @@
          **/
         @NonNull
         public Builder addIntAnnotation(final byte annotationId, final int value) {
-            if (0 == mPosLastField) {
+            if (mNumElements < 2) {
                 mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
             } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
                 mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
@@ -587,6 +607,7 @@
                 mCurrentAnnotationCount++;
                 writeAnnotationCount();
             }
+
             return this;
         }
 
@@ -619,19 +640,20 @@
                 mErrorMask |= ERROR_TOO_MANY_FIELDS;
             }
 
-            int size = mPos;
-            mPos = POS_TIMESTAMP_NS;
-            writeLong(mTimestampNs);
-            writeInt(mAtomId);
             if (0 == mErrorMask) {
                 mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
             } else {
+                // Write atom id and error mask. Overwrite any annotations for atom Id.
+                mPos = POS_ATOM_ID;
+                mPos += mBuffer.putByte(mPos, TYPE_INT);
+                mPos += mBuffer.putInt(mPos, mAtomId);
                 mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
                 mPos += mBuffer.putInt(mPos, mErrorMask);
                 mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
-                size = mPos;
             }
 
+            final int size = mPos;
+
             if (mUsePooledBuffer) {
                 return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
             } else {
diff --git a/apex/statsd/framework/test/src/android/util/StatsEventTest.java b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
index ac25e27..7b51155 100644
--- a/apex/statsd/framework/test/src/android/util/StatsEventTest.java
+++ b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
@@ -85,6 +85,45 @@
     }
 
     @Test
+    public void testOnlyAtomId() {
+        final int expectedAtomId = 109;
+
+        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+        final StatsEvent statsEvent = StatsEvent.newBuilder()
+                .setAtomId(expectedAtomId)
+                .usePooledBuffer()
+                .build();
+        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+        final ByteBuffer buffer =
+                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+        assertWithMessage("Incorrect number of elements in root object")
+                .that(buffer.get()).isEqualTo(2);
+
+        assertWithMessage("First element is not timestamp")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+        assertWithMessage("Incorrect timestamp")
+                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+        assertWithMessage("Second element is not atom id")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+
+        assertWithMessage("Incorrect atom id")
+                .that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+        statsEvent.release();
+    }
+
+    @Test
     public void testIntBooleanIntInt() {
         final int expectedAtomId = 109;
         final int field1 = 1;
@@ -460,6 +499,151 @@
         statsEvent.release();
     }
 
+    @Test
+    public void testAtomIdAnnotations() {
+        final int expectedAtomId = 109;
+        final byte atomAnnotationId = 84;
+        final int atomAnnotationValue = 9;
+        final int field1 = 1;
+        final byte field1AnnotationId = 45;
+        final boolean field1AnnotationValue = false;
+        final boolean field2 = true;
+        final byte field2AnnotationId = 1;
+        final int field2AnnotationValue = 23;
+
+        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+        final StatsEvent statsEvent = StatsEvent.newBuilder()
+                .setAtomId(expectedAtomId)
+                .addIntAnnotation(atomAnnotationId, atomAnnotationValue)
+                .writeInt(field1)
+                .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
+                .writeBoolean(field2)
+                .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
+                .usePooledBuffer()
+                .build();
+        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+        final ByteBuffer buffer =
+                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+        assertWithMessage("Incorrect number of elements in root object")
+                .that(buffer.get()).isEqualTo(4);
+
+        assertWithMessage("First element is not timestamp")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+        assertWithMessage("Incorrect timestamp")
+                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+        final byte atomIdHeader = buffer.get();
+        final int atomIdAnnotationValueCount = atomIdHeader >> 4;
+        final byte atomIdValueType = (byte) (atomIdHeader & 0x0F);
+        assertWithMessage("Second element is not atom id")
+                .that(atomIdValueType).isEqualTo(StatsEvent.TYPE_INT);
+        assertWithMessage("Atom id annotation count is wrong")
+                .that(atomIdAnnotationValueCount).isEqualTo(1);
+        assertWithMessage("Incorrect atom id")
+                .that(buffer.getInt()).isEqualTo(expectedAtomId);
+        assertWithMessage("Atom id's annotation id is wrong")
+                .that(buffer.get()).isEqualTo(atomAnnotationId);
+        assertWithMessage("Atom id's annotation type is wrong")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+        assertWithMessage("Atom id's annotation value is wrong")
+                .that(buffer.getInt()).isEqualTo(atomAnnotationValue);
+
+        final byte field1Header = buffer.get();
+        final int field1AnnotationValueCount = field1Header >> 4;
+        final byte field1Type = (byte) (field1Header & 0x0F);
+        assertWithMessage("First field is not Int")
+                .that(field1Type).isEqualTo(StatsEvent.TYPE_INT);
+        assertWithMessage("First field annotation count is wrong")
+                .that(field1AnnotationValueCount).isEqualTo(1);
+        assertWithMessage("Incorrect field 1")
+                .that(buffer.getInt()).isEqualTo(field1);
+        assertWithMessage("First field's annotation id is wrong")
+                .that(buffer.get()).isEqualTo(field1AnnotationId);
+        assertWithMessage("First field's annotation type is wrong")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
+        assertWithMessage("First field's annotation value is wrong")
+                .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0);
+
+        final byte field2Header = buffer.get();
+        final int field2AnnotationValueCount = field2Header >> 4;
+        final byte field2Type = (byte) (field2Header & 0x0F);
+        assertWithMessage("Second field is not boolean")
+                .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN);
+        assertWithMessage("Second field annotation count is wrong")
+                .that(field2AnnotationValueCount).isEqualTo(1);
+        assertWithMessage("Incorrect field 2")
+                .that(buffer.get()).isEqualTo(field2 ? 1 : 0);
+        assertWithMessage("Second field's annotation id is wrong")
+                .that(buffer.get()).isEqualTo(field2AnnotationId);
+        assertWithMessage("Second field's annotation type is wrong")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+        assertWithMessage("Second field's annotation value is wrong")
+                .that(buffer.getInt()).isEqualTo(field2AnnotationValue);
+
+        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+        statsEvent.release();
+    }
+
+    @Test
+    public void testSetAtomIdNotCalledImmediately() {
+        final int expectedAtomId = 109;
+        final int field1 = 25;
+        final boolean field2 = true;
+
+        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+        final StatsEvent statsEvent = StatsEvent.newBuilder()
+                .writeInt(field1)
+                .setAtomId(expectedAtomId)
+                .writeBoolean(field2)
+                .usePooledBuffer()
+                .build();
+        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+        final ByteBuffer buffer =
+                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+        assertWithMessage("Incorrect number of elements in root object")
+                .that(buffer.get()).isEqualTo(3);
+
+        assertWithMessage("First element is not timestamp")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+        assertWithMessage("Incorrect timestamp")
+                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+        assertWithMessage("Second element is not atom id")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+
+        assertWithMessage("Incorrect atom id")
+                .that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+        assertWithMessage("Third element is not errors type")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+
+        final int errorMask = buffer.getInt();
+
+        assertWithMessage("ERROR_ATOM_ID_INVALID_POSITION should be the only error in the mask")
+                .that(errorMask).isEqualTo(StatsEvent.ERROR_ATOM_ID_INVALID_POSITION);
+
+        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+        statsEvent.release();
+    }
+
     private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
         final int numBytes = buffer.getInt();
         byte[] bytes = new byte[numBytes];
diff --git a/api/current.txt b/api/current.txt
index c19a70c..c7e2e6f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7605,6 +7605,7 @@
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
     method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
+    method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
     method @IntRange(from=0) public long getRemainingLeaseQuotaBytes();
     method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
     method @NonNull public android.app.blob.BlobStoreManager.Session openSession(@IntRange(from=1) long) throws java.io.IOException;
@@ -25340,11 +25341,12 @@
   public final class MediaCodec.QueueRequest {
     method public void queue();
     method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
+    method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo);
     method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
     method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
     method @NonNull public android.media.MediaCodec.QueueRequest setHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
     method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
-    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int);
     method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
     method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
     method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0db..7e25382 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6061,7 +6061,7 @@
     method public void release();
   }
 
-  public class InvalidPacketException extends java.lang.Exception {
+  public final class InvalidPacketException extends java.lang.Exception {
     ctor public InvalidPacketException(int);
     method public int getError();
     field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6255,7 +6255,7 @@
     field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
   }
 
-  public static class NetworkCapabilities.Builder {
+  public static final class NetworkCapabilities.Builder {
     ctor public NetworkCapabilities.Builder();
     ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
     method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 38cf6a1..08a9197 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -598,7 +598,6 @@
 
   public class BlobStoreManager {
     method @Nullable public android.app.blob.LeaseInfo getLeaseInfo(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
-    method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
     method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
   }
 
@@ -1834,7 +1833,7 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
-  public static class NetworkCapabilities.Builder {
+  public static final class NetworkCapabilities.Builder {
     ctor public NetworkCapabilities.Builder();
     ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
     method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index d295b84..78c322e 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,6 +17,7 @@
 #include "Log.h"
 
 #include "FdBuffer.h"
+#include "incidentd_util.h"
 
 #include <log/log.h>
 #include <utils/SystemClock.h>
@@ -31,17 +32,24 @@
 namespace incidentd {
 
 const ssize_t BUFFER_SIZE = 16 * 1024;  // 16 KB
-const ssize_t MAX_BUFFER_COUNT = 6144;   // 96 MB max
+const ssize_t MAX_BUFFER_SIZE = 96 * 1024 * 1024;  // 96 MB
 
-FdBuffer::FdBuffer()
-        :mBuffer(new EncodedBuffer(BUFFER_SIZE)),
+FdBuffer::FdBuffer(): FdBuffer(get_buffer_from_pool(), /* isBufferPooled= */ true)  {
+}
+
+FdBuffer::FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled)
+        :mBuffer(buffer),
          mStartTime(-1),
          mFinishTime(-1),
          mTimedOut(false),
-         mTruncated(false) {
+         mTruncated(false),
+         mIsBufferPooled(isBufferPooled) {
 }
 
 FdBuffer::~FdBuffer() {
+    if (mIsBufferPooled) {
+        return_buffer_to_pool(mBuffer);
+    }
 }
 
 status_t FdBuffer::read(int fd, int64_t timeout) {
@@ -51,7 +59,7 @@
     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
 
     while (true) {
-        if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+        if (mBuffer->size() >= MAX_BUFFER_SIZE) {
             mTruncated = true;
             VLOG("Truncating data");
             break;
@@ -106,7 +114,7 @@
     mStartTime = uptimeMillis();
 
     while (true) {
-        if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+        if (mBuffer->size() >= MAX_BUFFER_SIZE) {
             // Don't let it get too big.
             mTruncated = true;
             VLOG("Truncating data");
@@ -156,7 +164,7 @@
 
     // This is the buffer used to store processed data
     while (true) {
-        if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+        if (mBuffer->size() >= MAX_BUFFER_SIZE) {
             VLOG("Truncating data");
             mTruncated = true;
             break;
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index a349360..9b2794d 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -35,6 +35,7 @@
 class FdBuffer {
 public:
     FdBuffer();
+    FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled = false);
     ~FdBuffer();
 
     /**
@@ -114,6 +115,7 @@
     int64_t mFinishTime;
     bool mTimedOut;
     bool mTruncated;
+    bool mIsBufferPooled;
 };
 
 }  // namespace incidentd
diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp
index d00ecdd..0d427d1 100644
--- a/cmds/incidentd/src/PrivacyFilter.cpp
+++ b/cmds/incidentd/src/PrivacyFilter.cpp
@@ -19,9 +19,6 @@
 #include "incidentd_util.h"
 #include "PrivacyFilter.h"
 #include "proto_util.h"
-
-#include "incidentd_util.h"
-#include "proto_util.h"
 #include "Section.h"
 
 #include <android-base/file.h>
@@ -129,6 +126,8 @@
     FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
             uint8_t bufferLevel);
 
+    ~FieldStripper();
+
     /**
      * Take the data that we have, and filter it down so that no fields
      * are more sensitive than the given privacy policy.
@@ -167,6 +166,7 @@
      */
     uint8_t mCurrentLevel;
 
+    sp<EncodedBuffer> mEncodedBuffer;
 };
 
 FieldStripper::FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
@@ -174,19 +174,25 @@
         :mRestrictions(restrictions),
          mData(data),
          mSize(data->size()),
-         mCurrentLevel(bufferLevel) {
+         mCurrentLevel(bufferLevel),
+         mEncodedBuffer(get_buffer_from_pool()) {
     if (mSize < 0) {
         ALOGW("FieldStripper constructed with a ProtoReader that doesn't support size."
                 " Data will be missing.");
     }
 }
 
+FieldStripper::~FieldStripper() {
+    return_buffer_to_pool(mEncodedBuffer);
+}
+
 status_t FieldStripper::strip(const uint8_t privacyPolicy) {
     // If the current strip level is less (fewer fields retained) than what's already in the
     // buffer, then we can skip it.
     if (mCurrentLevel < privacyPolicy) {
         PrivacySpec spec(privacyPolicy);
-        ProtoOutputStream proto;
+        mEncodedBuffer->clear();
+        ProtoOutputStream proto(mEncodedBuffer);
 
         // Optimization when no strip happens.
         if (mRestrictions == NULL || spec.RequireAll()) {
@@ -267,7 +273,7 @@
     // Order the writes by privacy filter, with increasing levels of filtration,k
     // so we can do the filter once, and then write many times.
     sort(mOutputs.begin(), mOutputs.end(),
-        [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool { 
+        [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool {
             return a->getPrivacyPolicy() < b->getPrivacyPolicy();
         });
 
@@ -370,7 +376,7 @@
             write_field_or_skip(NULL, reader, fieldTag, true);
         }
     }
-
+    clear_buffer_pool();
     err = reader->getError();
     if (err != NO_ERROR) {
         ALOGW("filter_and_write_report reader had an error: %s", strerror(-err));
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index ad25342..86a78f0 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -698,7 +698,7 @@
             listener->onReportFailed();
         });
     }
-
+    clear_buffer_pool();
     ALOGI("Done taking incident report err=%s", strerror(-err));
 }
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index c703c3c..dec9cb0 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -36,6 +36,7 @@
 #include <log/log_read.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
+#include <sys/mman.h>
 
 #include "FdBuffer.h"
 #include "Privacy.h"
@@ -106,7 +107,6 @@
         return NO_ERROR;
     }
 
-    FdBuffer buffer;
     Fpipe p2cPipe;
     Fpipe c2pPipe;
     // initiate pipes to pass data to/from incident_helper
@@ -122,6 +122,7 @@
     }
 
     // parent process
+    FdBuffer buffer;
     status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
                                                            std::move(c2pPipe.readFd()),
                                                            this->timeoutMs, mIsSysfs);
@@ -356,7 +357,6 @@
 CommandSection::~CommandSection() { free(mCommand); }
 
 status_t CommandSection::Execute(ReportWriter* writer) const {
-    FdBuffer buffer;
     Fpipe cmdPipe;
     Fpipe ihPipe;
 
@@ -377,6 +377,7 @@
     }
 
     cmdPipe.writeFd().reset();
+    FdBuffer buffer;
     status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
     writer->setSectionStats(buffer);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
@@ -574,6 +575,16 @@
 }
 
 status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
+    // heap profile shows that liblog malloc & free significant amount of memory in this process.
+    // Hence forking a new process to prevent memory fragmentation.
+    pid_t pid = fork();
+    if (pid < 0) {
+        ALOGW("[%s] failed to fork", this->name.string());
+        return errno;
+    }
+    if (pid > 0) {
+        return wait_child(pid, this->timeoutMs);
+    }
     // Open log buffer and getting logs since last retrieved time if any.
     unique_ptr<logger_list, void (*)(logger_list*)> loggers(
             gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
@@ -583,31 +594,31 @@
 
     if (android_logger_open(loggers.get(), mLogID) == NULL) {
         ALOGE("[%s] Can't get logger.", this->name.string());
-        return -1;
+        _exit(EXIT_FAILURE);
     }
 
     log_msg msg;
     log_time lastTimestamp(0);
 
     ProtoOutputStream proto;
+    status_t err = OK;
     while (true) {  // keeps reading until logd buffer is fully read.
-        status_t err = android_logger_list_read(loggers.get(), &msg);
-        // err = 0 - no content, unexpected connection drop or EOF.
-        // err = +ive number - size of retrieved data from logger
-        // err = -ive number, OS supplied error _except_ for -EAGAIN
-        // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data.
-        if (err <= 0) {
-            if (err != -EAGAIN) {
+        status_t status = android_logger_list_read(loggers.get(), &msg);
+        // status = 0 - no content, unexpected connection drop or EOF.
+        // status = +ive number - size of retrieved data from logger
+        // status = -ive number, OS supplied error _except_ for -EAGAIN
+        // status = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end.
+        if (status <= 0) {
+            if (status != -EAGAIN) {
                 ALOGW("[%s] fails to read a log_msg.\n", this->name.string());
+                err = -status;
             }
-            // dump previous logs and don't consider this error a failure.
             break;
         }
         if (mBinary) {
             // remove the first uint32 which is tag's index in event log tags
             android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t),
                                                                     msg.len() - sizeof(uint32_t));
-            ;
             android_log_list_element elem;
 
             lastTimestamp.tv_sec = msg.entry.sec;
@@ -667,9 +678,10 @@
             }
         } else {
             AndroidLogEntry entry;
-            err = android_log_processLogBuffer(&msg.entry, &entry);
-            if (err != NO_ERROR) {
+            status = android_log_processLogBuffer(&msg.entry, &entry);
+            if (status != OK) {
                 ALOGW("[%s] fails to process to an entry.\n", this->name.string());
+                err = status;
                 break;
             }
             lastTimestamp.tv_sec = entry.tv_sec;
@@ -688,17 +700,24 @@
                         trimTail(entry.message, entry.messageLen));
             proto.end(token);
         }
+        if (!proto.flush(pipeWriteFd.get())) {
+            if (errno == EPIPE) {
+                ALOGW("[%s] wrote to a broken pipe\n", this->name.string());
+            }
+            err = errno;
+            break;
+        }
+        proto.clear();
     }
     gLastLogsRetrieved[mLogID] = lastTimestamp;
-    if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
-        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
-        return EPIPE;
-    }
-    return NO_ERROR;
+    _exit(err);
 }
 
 // ================================================================================
 
+const int LINK_NAME_LEN = 64;
+const int EXE_NAME_LEN = 1024;
+
 TombstoneSection::TombstoneSection(int id, const char* type, const int64_t timeoutMs)
     : WorkerThreadSection(id, timeoutMs), mType(type) {
     name = "tombstone ";
@@ -716,25 +735,37 @@
 
     const std::set<int> hal_pids = get_interesting_hal_pids();
 
-    ProtoOutputStream proto;
+    auto pooledBuffer = get_buffer_from_pool();
+    ProtoOutputStream proto(pooledBuffer);
+    // dumpBufferSize should be a multiple of page size (4 KB) to reduce memory fragmentation
+    size_t dumpBufferSize = 64 * 1024; // 64 KB is enough for most tombstone dump
+    char* dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
     struct dirent* d;
+    char link_name[LINK_NAME_LEN];
+    char exe_name[EXE_NAME_LEN];
     status_t err = NO_ERROR;
     while ((d = readdir(proc.get()))) {
         int pid = atoi(d->d_name);
         if (pid <= 0) {
             continue;
         }
-
-        const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
-        std::string exe;
-        if (!android::base::Readlink(link_name, &exe)) {
-            ALOGE("Section %s: Can't read '%s': %s\n", name.string(),
-                    link_name.c_str(), strerror(errno));
+        snprintf(link_name, LINK_NAME_LEN, "/proc/%d/exe", pid);
+        struct stat fileStat;
+        if (stat(link_name, &fileStat) != OK) {
             continue;
         }
+        size_t exe_name_len = readlink(link_name, exe_name, EXE_NAME_LEN);
+        if (exe_name_len < 0 || exe_name_len >= EXE_NAME_LEN) {
+            ALOGE("[%s] Can't read '%s': %s", name.string(), link_name, strerror(errno));
+            continue;
+        }
+        // readlink(2) does not put a null terminator at the end
+        exe_name[exe_name_len] = '\0';
 
         bool is_java_process;
-        if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+        if (strncmp(exe_name, "/system/bin/app_process32", LINK_NAME_LEN) == 0 ||
+                strncmp(exe_name, "/system/bin/app_process64", LINK_NAME_LEN) == 0) {
             if (mType != "java") continue;
             // Don't bother dumping backtraces for the zygote.
             if (IsZygote(pid)) {
@@ -743,7 +774,7 @@
             }
 
             is_java_process = true;
-        } else if (should_dump_native_traces(exe.c_str())) {
+        } else if (should_dump_native_traces(exe_name)) {
             if (mType != "native") continue;
             is_java_process = false;
         } else if (hal_pids.find(pid) != hal_pids.end()) {
@@ -799,29 +830,37 @@
             ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
         }
 
-        auto dump = std::make_unique<char[]>(buffer.size());
+        // Resize dump buffer
+        if (dumpBufferSize < buffer.size()) {
+            munmap(dumpBuffer, dumpBufferSize);
+            while(dumpBufferSize < buffer.size()) dumpBufferSize = dumpBufferSize << 1;
+            dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+        }
         sp<ProtoReader> reader = buffer.data()->read();
         int i = 0;
         while (reader->hasNext()) {
-            dump[i] = reader->next();
+            dumpBuffer[i] = reader->next();
             i++;
         }
         uint64_t token = proto.start(android::os::BackTraceProto::TRACES);
         proto.write(android::os::BackTraceProto::Stack::PID, pid);
-        proto.write(android::os::BackTraceProto::Stack::DUMP, dump.get(), i);
+        proto.write(android::os::BackTraceProto::Stack::DUMP, dumpBuffer, i);
         proto.write(android::os::BackTraceProto::Stack::DUMP_DURATION_NS,
                     static_cast<long long>(Nanotime() - start));
         proto.end(token);
         dumpPipe.readFd().reset();
-    }
-
-    if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
-        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
-        if (err != NO_ERROR) {
-            return EPIPE;
+        if (!proto.flush(pipeWriteFd.get())) {
+            if (errno == EPIPE) {
+                ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+            }
+            err = errno;
+            break;
         }
+        proto.clear();
     }
-
+    munmap(dumpBuffer, dumpBufferSize);
+    return_buffer_to_pool(pooledBuffer);
     return err;
 }
 
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 2649fb9..150ab99 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -18,6 +18,7 @@
 
 #include "incidentd_util.h"
 
+#include <android/util/EncodedBuffer.h>
 #include <fcntl.h>
 #include <sys/prctl.h>
 #include <wait.h>
@@ -28,8 +29,6 @@
 namespace os {
 namespace incidentd {
 
-using namespace android::base;
-
 const Privacy* get_privacy_of_section(int id) {
     int l = 0;
     int r = PRIVACY_POLICY_COUNT - 1;
@@ -48,6 +47,30 @@
     return NULL;
 }
 
+std::vector<sp<EncodedBuffer>> gBufferPool;
+std::mutex gBufferPoolLock;
+
+sp<EncodedBuffer> get_buffer_from_pool() {
+    std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+    if (gBufferPool.size() == 0) {
+        return new EncodedBuffer();
+    }
+    sp<EncodedBuffer> buffer = gBufferPool.back();
+    gBufferPool.pop_back();
+    return buffer;
+}
+
+void return_buffer_to_pool(sp<EncodedBuffer> buffer) {
+    buffer->clear();
+    std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+    gBufferPool.push_back(buffer);
+}
+
+void clear_buffer_pool() {
+    std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+    gBufferPool.clear();
+}
+
 // ================================================================================
 Fpipe::Fpipe() : mRead(), mWrite() {}
 
@@ -84,7 +107,7 @@
         status = &dummy_status;
     }
     *status = 0;
-    pid_t pid = fork();
+    pid_t pid = vfork();
     if (pid < 0) {
         *status = -errno;
         return -1;
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index a54993fe..8499889 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -19,18 +19,21 @@
 #define INCIDENTD_UTIL_H
 
 #include <stdarg.h>
-#include <unistd.h>
-
-#include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 
 #include "Privacy.h"
 
 namespace android {
+
+namespace util {
+class EncodedBuffer;
+}
+
 namespace os {
 namespace incidentd {
 
-using namespace android::base;
+using android::base::unique_fd;
+using android::util::EncodedBuffer;
 
 /**
  * Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST;
@@ -38,6 +41,25 @@
 const Privacy* get_privacy_of_section(int id);
 
 /**
+ * Get an EncodedBuffer from an internal pool, or create and return a new one if the pool is empty.
+ * The EncodedBuffer should be returned after use.
+ * Thread safe.
+ */
+sp<EncodedBuffer> get_buffer_from_pool();
+
+/**
+ * Return the EncodedBuffer back to the pool for reuse.
+ * Thread safe.
+ */
+void return_buffer_to_pool(sp<EncodedBuffer> buffer);
+
+/**
+ * Clear the buffer pool to free memory, after taking an incident report.
+ * Thread safe.
+ */
+void clear_buffer_pool();
+
+/**
  * This class wraps android::base::Pipe.
  */
 class Fpipe {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f9711c3..b3d534a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -78,7 +78,8 @@
     // Pushed atoms start at 2.
     oneof pushed {
         // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
-        BleScanStateChanged ble_scan_state_changed = 2 [(module) = "bluetooth"];
+        BleScanStateChanged ble_scan_state_changed = 2
+                [(module) = "bluetooth", (module) = "statsdtest"];
         ProcessStateChanged process_state_changed = 3 [(module) = "framework"];
         BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
         SensorStateChanged sensor_state_changed =
@@ -434,7 +435,7 @@
         ProcessMemoryState process_memory_state = 10013 [(module) = "framework"];
         SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"];
         SystemUptime system_uptime = 10015 [(module) = "framework", (module) = "statsdtest"];
-        CpuActiveTime cpu_active_time = 10016 [(module) = "framework"];
+        CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"];
         CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework", (module) = "statsdtest"];
         DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"];
         RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
@@ -4589,6 +4590,8 @@
     optional int64 tx_bytes = 4;
 
     optional int64 tx_packets = 5;
+
+    optional int32 rat_type = 6;
 }
 
 /**
@@ -4611,6 +4614,8 @@
     optional int64 tx_bytes = 5;
 
     optional int64 tx_packets = 6;
+
+    optional int32 rat_type = 7;
 }
 
 /**
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index a5ff067..0bf24f1 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 #include <gtest/gtest.h>
+
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/matcher_util.h"
 #include "src/logd/LogEvent.h"
+#include "stats_event.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 #include "subscriber/SubscriberReporter.h"
+#include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
 
@@ -30,6 +33,58 @@
 namespace os {
 namespace statsd {
 
+namespace {
+void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                  const vector<int>& attributionUids, const vector<string>& attributionTags,
+                  const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                  const vector<int>& attributionUids, const vector<string>& attributionTags,
+                  const int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+}  // anonymous namespace
+
 TEST(AtomMatcherTest, TestFieldTranslation) {
     FieldMatcher matcher1;
     matcher1.set_field(10);
@@ -72,66 +127,50 @@
     EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask);
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestFilter_ALL) {
-//    FieldMatcher matcher1;
-//    matcher1.set_field(10);
-//    FieldMatcher* child = matcher1.add_child();
-//    child->set_field(1);
-//    child->set_position(Position::ALL);
-//
-//    child->add_child()->set_field(1);
-//    child->add_child()->set_field(2);
-//
-//    child = matcher1.add_child();
-//    child->set_field(2);
-//
-//    vector<Matcher> matchers;
-//    translateFieldMatcher(matcher1, &matchers);
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(10, 12345);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//    HashableDimensionKey output;
-//
-//    filterValues(matchers, event.getValues(), &output);
-//
-//    EXPECT_EQ((size_t)7, output.getValues().size());
-//    EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
-//    EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
-//    EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
-//    EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
-//    EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
-//    EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
-//    EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
-//    EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
-//}
+TEST(AtomMatcherTest, TestFilter_ALL) {
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::ALL);
+
+    child->add_child()->set_field(1);
+    child->add_child()->set_field(2);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers;
+    translateFieldMatcher(matcher1, &matchers);
+
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
+    HashableDimensionKey output;
+
+    filterValues(matchers, event.getValues(), &output);
+
+    EXPECT_EQ((size_t)7, output.getValues().size());
+    EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
+    EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
+    EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
+    EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
+    EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
+    EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
+    EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
+}
 
 TEST(AtomMatcherTest, TestSubDimension) {
     HashableDimensionKey dim;
@@ -174,61 +213,45 @@
     EXPECT_TRUE(dim.contains(subDim4));
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestMetric2ConditionLink) {
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(10, 12345);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    FieldMatcher whatMatcher;
-//    whatMatcher.set_field(10);
-//    FieldMatcher* child11 = whatMatcher.add_child();
-//    child11->set_field(1);
-//    child11->set_position(Position::ANY);
-//    child11 = child11->add_child();
-//    child11->set_field(1);
-//
-//    FieldMatcher conditionMatcher;
-//    conditionMatcher.set_field(27);
-//    FieldMatcher* child2 = conditionMatcher.add_child();
-//    child2->set_field(2);
-//    child2->set_position(Position::LAST);
-//
-//    child2 = child2->add_child();
-//    child2->set_field(2);
-//
-//    Metric2Condition link;
-//
-//    translateFieldMatcher(whatMatcher, &link.metricFields);
-//    translateFieldMatcher(conditionMatcher, &link.conditionFields);
-//
-//    EXPECT_EQ((size_t)1, link.metricFields.size());
-//    EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
-//    EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
-//    EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
-//
-//    EXPECT_EQ((size_t)1, link.conditionFields.size());
-//    EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
-//    EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
-//    EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
-//}
+TEST(AtomMatcherTest, TestMetric2ConditionLink) {
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value");
+
+    FieldMatcher whatMatcher;
+    whatMatcher.set_field(10);
+    FieldMatcher* child11 = whatMatcher.add_child();
+    child11->set_field(1);
+    child11->set_position(Position::ANY);
+    child11 = child11->add_child();
+    child11->set_field(1);
+
+    FieldMatcher conditionMatcher;
+    conditionMatcher.set_field(27);
+    FieldMatcher* child2 = conditionMatcher.add_child();
+    child2->set_field(2);
+    child2->set_position(Position::LAST);
+
+    child2 = child2->add_child();
+    child2->set_field(2);
+
+    Metric2Condition link;
+
+    translateFieldMatcher(whatMatcher, &link.metricFields);
+    translateFieldMatcher(conditionMatcher, &link.conditionFields);
+
+    EXPECT_EQ((size_t)1, link.metricFields.size());
+    EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
+    EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
+
+    EXPECT_EQ((size_t)1, link.conditionFields.size());
+    EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
+    EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
+}
 
 TEST(AtomMatcherTest, TestWriteDimensionPath) {
     for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) {
@@ -439,50 +462,38 @@
     EXPECT_EQ(99999, dim4.value_long());
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestWriteAtomToProto) {
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2};
-//
-//    // Set up the event
-//    LogEvent event(4, 12345);
-//    event.write(attribution_nodes);
-//    event.write((int32_t)999);
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    android::util::ProtoOutputStream protoOutput;
-//    writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
-//
-//    vector<uint8_t> outData;
-//    outData.resize(protoOutput.size());
-//    size_t pos = 0;
-//    sp<ProtoReader> reader = protoOutput.data();
-//    while (reader->readBuffer() != NULL) {
-//        size_t toRead = reader->currentToRead();
-//        std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
-//        pos += toRead;
-//        reader->move(toRead);
-//    }
-//
-//    Atom result;
-//    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
-//    EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
-//    const auto& atom = result.ble_scan_result_received();
-//    EXPECT_EQ(2, atom.attribution_node_size());
-//    EXPECT_EQ(1111, atom.attribution_node(0).uid());
-//    EXPECT_EQ("location1", atom.attribution_node(0).tag());
-//    EXPECT_EQ(2222, atom.attribution_node(1).uid());
-//    EXPECT_EQ("location2", atom.attribution_node(1).tag());
-//    EXPECT_EQ(999, atom.num_results());
-//}
+TEST(AtomMatcherTest, TestWriteAtomToProto) {
+    std::vector<int> attributionUids = {1111, 2222};
+    std::vector<string> attributionTags = {"location1", "location2"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999);
+
+    android::util::ProtoOutputStream protoOutput;
+    writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
+
+    vector<uint8_t> outData;
+    outData.resize(protoOutput.size());
+    size_t pos = 0;
+    sp<ProtoReader> reader = protoOutput.data();
+    while (reader->readBuffer() != NULL) {
+        size_t toRead = reader->currentToRead();
+        std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
+        pos += toRead;
+        reader->move(toRead);
+    }
+
+    Atom result;
+    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+    EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
+    const auto& atom = result.ble_scan_result_received();
+    EXPECT_EQ(2, atom.attribution_node_size());
+    EXPECT_EQ(1111, atom.attribution_node(0).uid());
+    EXPECT_EQ("location1", atom.attribution_node(0).tag());
+    EXPECT_EQ(2222, atom.attribution_node(1).uid());
+    EXPECT_EQ("location2", atom.attribution_node(1).tag());
+    EXPECT_EQ(999, atom.num_results());
+}
 
 /*
  * Test two Matchers is not a subset of one Matcher.
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 2de6377..6f4c8e4 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -12,18 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/matcher_util.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
-
 #include <stdio.h>
 
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
+#include "stats_event.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+#include "statsd_test_util.h"
+
 using namespace android::os::statsd;
 using std::unordered_map;
 using std::vector;
@@ -39,646 +40,691 @@
 
 
 #ifdef __ANDROID__
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(AtomMatcherTest, TestSimpleMatcher) {
-//    UidMap uidMap;
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    LogEvent event(TAG_ID, 0);
-//    EXPECT_TRUE(event.write(11));
-//    event.init();
-//
-//    // Test
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Wrong tag id.
-//    simpleMatcher->set_atom_id(TAG_ID + 1);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestAttributionMatcher) {
-//    UidMap uidMap;
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//        ATTRIBUTION_TAG_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    // Tag not matched.
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Match last node.
-//    attributionMatcher->set_position(Position::LAST);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Match any node.
-//    attributionMatcher->set_position(Position::ANY);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location4");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Attribution match but primitive field not match.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location2");
-//    fieldMatcher->set_eq_string("wrong value");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    fieldMatcher->set_eq_string("some value");
-//
-//    // Uid match.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
-//        ATTRIBUTION_UID_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Uid + tag.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//        ATTRIBUTION_TAG_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestUidFieldMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//        1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//        {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//         android::String16("v1"), android::String16("v2")},
-//        {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//         android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//        {android::String16(""), android::String16(""), android::String16(""),
-//         android::String16(""), android::String16("")});
-//
-//    // Set up matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    simpleMatcher->add_field_value_matcher()->set_field(1);
-//    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(1111);
-//    event.init();
-//
-//    LogEvent event2(TAG_ID_2, 0);
-//    event2.write(1111);
-//    event2.write("some value");
-//    event2.init();
-//
-//    // Tag not in kAtomsWithUidField
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Tag found in kAtomsWithUidField and has matching uid
-//    simpleMatcher->set_atom_id(TAG_ID_2);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-//
-//    // Tag found in kAtomsWithUidField but has non-matching uid
-//    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
-//}
-//
-//TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//
-//    AttributionNodeInternal attribution_node4;
-//    attribution_node4.set_uid(1066);
-//    attribution_node4.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3, attribution_node4};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//            ATTRIBUTION_UID_FIELD_ID);
-//    auto neqStringList = attributionMatcher->mutable_matches_tuple()
-//                                 ->mutable_field_value_matcher(0)
-//                                 ->mutable_neq_any_string();
-//    neqStringList->add_str_value("pkg2");
-//    neqStringList->add_str_value("pkg3");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("pkg1");
-//    neqStringList->add_str_value("pkg3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::ANY);
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("maps.com");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("PkG3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("AID_STATSD");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1067);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//
-//    AttributionNodeInternal attribution_node4;
-//    attribution_node4.set_uid(1066);
-//    attribution_node4.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3, attribution_node4};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//            ATTRIBUTION_UID_FIELD_ID);
-//    auto eqStringList = attributionMatcher->mutable_matches_tuple()
-//                                ->mutable_field_value_matcher(0)
-//                                ->mutable_eq_any_string();
-//    eqStringList->add_str_value("AID_ROOT");
-//    eqStringList->add_str_value("AID_INCIDENTD");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::ANY);
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("AID_STATSD");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    auto normalStringField = fieldMatcher->mutable_eq_any_string();
-//    normalStringField->add_str_value("some value123");
-//    normalStringField->add_str_value("some value");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    normalStringField->Clear();
-//    normalStringField->add_str_value("AID_STATSD");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("maps.com");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestBoolMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue1 = simpleMatcher->add_field_value_matcher();
-//    keyValue1->set_field(FIELD_ID_1);
-//    auto keyValue2 = simpleMatcher->add_field_value_matcher();
-//    keyValue2->set_field(FIELD_ID_2);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    EXPECT_TRUE(event.write(true));
-//    EXPECT_TRUE(event.write(false));
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    keyValue1->set_eq_bool(true);
-//    keyValue2->set_eq_bool(false);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(false);
-//    keyValue2->set_eq_bool(false);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(false);
-//    keyValue2->set_eq_bool(true);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(true);
-//    keyValue2->set_eq_bool(true);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestStringMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//    keyValue->set_eq_string("some value");
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue1 = simpleMatcher->add_field_value_matcher();
-//    keyValue1->set_field(FIELD_ID_1);
-//    auto keyValue2 = simpleMatcher->add_field_value_matcher();
-//    keyValue2->set_field(FIELD_ID_2);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(2);
-//    event.write(3);
-//
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    keyValue1->set_eq_int(2);
-//    keyValue2->set_eq_int(3);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_int(2);
-//    keyValue2->set_eq_int(4);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_int(4);
-//    keyValue2->set_eq_int(3);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestIntComparisonMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(11);
-//    event.init();
-//
-//    // Test
-//
-//    // eq_int
-//    keyValue->set_eq_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_eq_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_eq_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // lt_int
-//    keyValue->set_lt_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lt_int(11);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lt_int(12);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // lte_int
-//    keyValue->set_lte_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lte_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lte_int(12);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // gt_int
-//    keyValue->set_gt_int(10);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gt_int(11);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gt_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // gte_int
-//    keyValue->set_gte_int(10);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gte_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gte_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//
-//    LogEvent event1(TAG_ID, 0);
-//    keyValue->set_lt_float(10.0);
-//    event1.write(10.1f);
-//    event1.init();
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
-//
-//    LogEvent event2(TAG_ID, 0);
-//    event2.write(9.9f);
-//    event2.init();
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-//
-//    LogEvent event3(TAG_ID, 0);
-//    event3.write(10.1f);
-//    event3.init();
-//    keyValue->set_gt_float(10.0);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
-//
-//    LogEvent event4(TAG_ID, 0);
-//    event4.write(9.9f);
-//    event4.init();
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
-//}
+
+namespace {
+
+void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                     const int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                       const float floatValue) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeFloat(statsEvent, floatValue);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                        const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                           const int32_t value, const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                             const vector<int>& attributionUids,
+                             const vector<string>& attributionTags, const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                      const bool bool1, const bool bool2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeBool(statsEvent, bool1);
+    AStatsEvent_writeBool(statsEvent, bool2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+}  // anonymous namespace
+
+TEST(AtomMatcherTest, TestSimpleMatcher) {
+    UidMap uidMap;
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event, TAG_ID, 0, 11);
+
+    // Test
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Wrong tag id.
+    simpleMatcher->set_atom_id(TAG_ID + 1);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestAttributionMatcher) {
+    UidMap uidMap;
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    // Set up the log event.
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "tag");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    // Tag not matched.
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match last node.
+    attributionMatcher->set_position(Position::LAST);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match any node.
+    attributionMatcher->set_position(Position::ANY);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location4");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Attribution match but primitive field not match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location2");
+    fieldMatcher->set_eq_string("wrong value");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    fieldMatcher->set_eq_string("some value");
+
+    // Uid match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Uid + tag.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestUidFieldMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    // Set up matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    simpleMatcher->add_field_value_matcher()->set_field(1);
+    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+
+    // Set up the event
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event1, TAG_ID, 0, 1111);
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value");
+
+    // Tag not in kAtomsWithUidField
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+    // Tag found in kAtomsWithUidField and has matching uid
+    simpleMatcher->set_atom_id(TAG_ID_2);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+    // Tag found in kAtomsWithUidField but has non-matching uid
+    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+}
+
+TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    std::vector<int> attributionUids = {1111, 2222, 3333, 1066};
+    std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    auto neqStringList = attributionMatcher->mutable_matches_tuple()
+                                 ->mutable_field_value_matcher(0)
+                                 ->mutable_neq_any_string();
+    neqStringList->add_str_value("pkg2");
+    neqStringList->add_str_value("pkg3");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    neqStringList->Clear();
+    neqStringList->add_str_value("pkg1");
+    neqStringList->add_str_value("pkg3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::ANY);
+    neqStringList->Clear();
+    neqStringList->add_str_value("maps.com");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    neqStringList->Clear();
+    neqStringList->add_str_value("PkG3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    neqStringList->Clear();
+    neqStringList->add_str_value("AID_STATSD");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    std::vector<int> attributionUids = {1067, 2222, 3333, 1066};
+    std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    auto eqStringList = attributionMatcher->mutable_matches_tuple()
+                                ->mutable_field_value_matcher(0)
+                                ->mutable_eq_any_string();
+    eqStringList->add_str_value("AID_ROOT");
+    eqStringList->add_str_value("AID_INCIDENTD");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::ANY);
+    eqStringList->Clear();
+    eqStringList->add_str_value("AID_STATSD");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    eqStringList->Clear();
+    eqStringList->add_str_value("pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    auto normalStringField = fieldMatcher->mutable_eq_any_string();
+    normalStringField->add_str_value("some value123");
+    normalStringField->add_str_value("some value");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    normalStringField->Clear();
+    normalStringField->add_str_value("AID_STATSD");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    eqStringList->Clear();
+    eqStringList->add_str_value("maps.com");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestBoolMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeBoolLogEvent(&event, TAG_ID, 0, true, false);
+
+    // Test
+    keyValue1->set_eq_bool(true);
+    keyValue2->set_eq_bool(false);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(false);
+    keyValue2->set_eq_bool(false);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(false);
+    keyValue2->set_eq_bool(true);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(true);
+    keyValue2->set_eq_bool(true);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestStringMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+    keyValue->set_eq_string("some value");
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeStringLogEvent(&event, TAG_ID, 0, "some value");
+
+    // Test
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3);
+
+    // Test
+    keyValue1->set_eq_int(2);
+    keyValue2->set_eq_int(3);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_int(2);
+    keyValue2->set_eq_int(4);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_int(4);
+    keyValue2->set_eq_int(3);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestIntComparisonMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event, TAG_ID, 0, 11);
+
+    // Test
+
+    // eq_int
+    keyValue->set_eq_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_eq_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_eq_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // lt_int
+    keyValue->set_lt_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lt_int(11);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lt_int(12);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // lte_int
+    keyValue->set_lte_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lte_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lte_int(12);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // gt_int
+    keyValue->set_gt_int(10);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gt_int(11);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gt_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // gte_int
+    keyValue->set_gte_int(10);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gte_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gte_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
+    keyValue->set_lt_float(10.0);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
+    keyValue->set_gt_float(10.0);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+}
 
 // Helper for the composite matchers.
 void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index d6498f4..9e7b7c8 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -253,1416 +253,1420 @@
     EXPECT_EQ(2, report.annotation(0).field_int32());
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
-//    // Setup a simple config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(123456);
-//    countMetric->set_what(wakelockAcquireMatcher.id());
-//    countMetric->set_bucket(FIVE_MINUTES);
-//
-//    ConfigKey cfgKey;
-//    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
-//    processor->OnLogEvent(event.get());
-//
-//    vector<uint8_t> bytes;
-//    ConfigMetricsReportList output;
-//
-//    // Dump report WITHOUT erasing data.
-//    processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    EXPECT_EQ(output.reports_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-//
-//    // Dump report WITH erasing data. There should be data since we didn't previously erase it.
-//    processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    EXPECT_EQ(output.reports_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-//
-//    // Dump report again. There should be no data since we erased it.
-//    processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    // We don't care whether statsd has a report, as long as it has no count metrics in it.
-//    bool noData = output.reports_size() == 0
-//            || output.reports(0).metrics_size() == 0
-//            || output.reports(0).metrics(0).count_metrics().data_size() == 0;
-//    EXPECT_TRUE(noData);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
-//    int uid = 1111;
-//
-//    // Setup a simple config, no activation
-//    StatsdConfig config1;
-//    int64_t cfgId1 = 12341;
-//    config1.set_id(cfgId1);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    ConfigKey cfgKey1(uid, cfgId1);
-//
-//    // Add another config, with two metrics, one with activation
-//    StatsdConfig config2;
-//    int64_t cfgId2 = 12342;
-//    config2.set_id(cfgId2);
-//    config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    *config2.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId3 = 1234561;
-//    long metricId4 = 1234562;
-//
-//    auto countMetric3 = config2.add_count_metric();
-//    countMetric3->set_id(metricId3);
-//    countMetric3->set_what(wakelockAcquireMatcher.id());
-//    countMetric3->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric4 = config2.add_count_metric();
-//    countMetric4->set_id(metricId4);
-//    countMetric4->set_what(wakelockAcquireMatcher.id());
-//    countMetric4->set_bucket(FIVE_MINUTES);
-//
-//    auto metric3Activation = config2.add_metric_activation();
-//    metric3Activation->set_metric_id(metricId3);
-//    metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric3ActivationTrigger = metric3Activation->add_event_activation();
-//    metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric3ActivationTrigger->set_ttl_seconds(100);
-//
-//    ConfigKey cfgKey2(uid, cfgId2);
-//
-//    // Add another config, with two metrics, both with activations
-//    StatsdConfig config3;
-//    int64_t cfgId3 = 12343;
-//    config3.set_id(cfgId3);
-//    config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    *config3.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId5 = 1234565;
-//    long metricId6 = 1234566;
-//    auto countMetric5 = config3.add_count_metric();
-//    countMetric5->set_id(metricId5);
-//    countMetric5->set_what(wakelockAcquireMatcher.id());
-//    countMetric5->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric6 = config3.add_count_metric();
-//    countMetric6->set_id(metricId6);
-//    countMetric6->set_what(wakelockAcquireMatcher.id());
-//    countMetric6->set_bucket(FIVE_MINUTES);
-//
-//    auto metric5Activation = config3.add_metric_activation();
-//    metric5Activation->set_metric_id(metricId5);
-//    metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric5ActivationTrigger = metric5Activation->add_event_activation();
-//    metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric5ActivationTrigger->set_ttl_seconds(100);
-//
-//    auto metric6Activation = config3.add_metric_activation();
-//    metric6Activation->set_metric_id(metricId6);
-//    metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric6ActivationTrigger = metric6Activation->add_event_activation();
-//    metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric6ActivationTrigger->set_ttl_seconds(200);
-//
-//    ConfigKey cfgKey3(uid, cfgId3);
-//
-//    sp<UidMap> m = new UidMap();
-//    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-//    sp<AlarmMonitor> anomalyAlarmMonitor;
-//    sp<AlarmMonitor> subscriberAlarmMonitor;
-//    vector<int64_t> activeConfigsBroadcast;
-//
-//    long timeBase1 = 1;
-//    int broadcastCount = 0;
-//    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
-//            timeBase1, [](const ConfigKey& key) { return true; },
-//            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
-//                    const vector<int64_t>& activeConfigs) {
-//                broadcastCount++;
-//                EXPECT_EQ(broadcastUid, uid);
-//                activeConfigsBroadcast.clear();
-//                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
-//                        activeConfigs.begin(), activeConfigs.end());
-//                return true;
-//            });
-//
-//    processor.OnConfigUpdated(1, cfgKey1, config1);
-//    processor.OnConfigUpdated(2, cfgKey2, config2);
-//    processor.OnConfigUpdated(3, cfgKey3, config3);
-//
-//    EXPECT_EQ(3, processor.mMetricsManagers.size());
-//
-//    // Expect the first config and both metrics in it to be active.
-//    auto it = processor.mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_TRUE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
-//    it = processor.mMetricsManagers.find(cfgKey2);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//
-//    metricIt = metricsManager2->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId3) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
-//    auto& metricProducer3 = *metricIt;
-//    EXPECT_FALSE(metricProducer3->isActive());
-//
-//    metricIt = metricsManager2->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId4) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
-//    auto& metricProducer4 = *metricIt;
-//    EXPECT_TRUE(metricProducer4->isActive());
-//
-//    // Expect the third config and both metrics in it to be inactive.
-//    it = processor.mMetricsManagers.find(cfgKey3);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager3 = it->second;
-//    EXPECT_FALSE(metricsManager3->isActive());
-//
-//    metricIt = metricsManager3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId5) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
-//    auto& metricProducer5 = *metricIt;
-//    EXPECT_FALSE(metricProducer5->isActive());
-//
-//    metricIt = metricsManager3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId6) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
-//    auto& metricProducer6 = *metricIt;
-//    EXPECT_FALSE(metricProducer6->isActive());
-//
-//    // No broadcast for active configs should have happened yet.
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activate all 3 metrics that were not active.
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor.OnLogEvent(event.get());
-//
-//    // Assert that all 3 configs are active.
-//    EXPECT_TRUE(metricsManager1->isActive());
-//    EXPECT_TRUE(metricsManager2->isActive());
-//    EXPECT_TRUE(metricsManager3->isActive());
-//
-//    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
-//            != activeConfigsBroadcast.end());
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
-//            != activeConfigsBroadcast.end());
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
-//            != activeConfigsBroadcast.end());
-//
-//    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor.SaveActiveConfigsToDisk(shutDownTime);
-//    const int64_t ttl3 = event->GetElapsedTimestampNs() +
-//            metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    const int64_t ttl5 = event->GetElapsedTimestampNs() +
-//            metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    const int64_t ttl6 = event->GetElapsedTimestampNs() +
-//            metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//
-//    // Create a second StatsLogProcessor and push the same 3 configs.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//    processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
-//    processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
-//
-//    EXPECT_EQ(3, processor2->mMetricsManagers.size());
-//
-//    // First config and both metrics are active.
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    // Second config is active. Metric 3 is inactive, metric 4 is active.
-//    it = processor2->mMetricsManagers.find(cfgKey2);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1002 = it->second;
-//    EXPECT_TRUE(metricsManager1002->isActive());
-//
-//    metricIt = metricsManager1002->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId3) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
-//    auto& metricProducer1003 = *metricIt;
-//    EXPECT_FALSE(metricProducer1003->isActive());
-//
-//    metricIt = metricsManager1002->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId4) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
-//    auto& metricProducer1004 = *metricIt;
-//    EXPECT_TRUE(metricProducer1004->isActive());
-//
-//    // Config 3 is inactive. both metrics are inactive.
-//    it = processor2->mMetricsManagers.find(cfgKey3);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1003 = it->second;
-//    EXPECT_FALSE(metricsManager1003->isActive());
-//    EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
-//
-//    metricIt = metricsManager1003->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId5) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
-//    auto& metricProducer1005 = *metricIt;
-//    EXPECT_FALSE(metricProducer1005->isActive());
-//
-//    metricIt = metricsManager1003->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId6) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
-//    auto& metricProducer1006 = *metricIt;
-//    EXPECT_FALSE(metricProducer1006->isActive());
-//
-//    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
-//    EXPECT_FALSE(metricProducer1003->isActive());
-//    const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
-//    EXPECT_EQ(0, activation1003->start_ns);
-//    EXPECT_FALSE(metricProducer1005->isActive());
-//    const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
-//    EXPECT_EQ(0, activation1005->start_ns);
-//    EXPECT_FALSE(metricProducer1006->isActive());
-//    const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
-//    EXPECT_EQ(0, activation1006->start_ns);
-//
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // After loading activations from disk, assert that all 3 metrics are active.
-//    EXPECT_TRUE(metricProducer1003->isActive());
-//    EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
-//    EXPECT_TRUE(metricProducer1005->isActive());
-//    EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
-//    EXPECT_TRUE(metricProducer1006->isActive());
-//    EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
-//
-//    // Make sure no more broadcasts have happened.
-//    EXPECT_EQ(broadcastCount, 1);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBoot) {
-//    int uid = 1111;
-//
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger->set_ttl_seconds(100);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor->OnLogEvent(event.get());
-//
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
-//
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
-//    EXPECT_EQ(0, activation1001->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001->state);
-//
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
-//    EXPECT_EQ(kActive, activation1001->state);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
-//    int uid = 1111;
-//
-//    // Create config with 2 metrics:
-//    // Metric 1: Activate on boot with 2 activations
-//    // Metric 2: Always active
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    int i = 0;
-//    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//
-//    i = 0;
-//    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//
-//    EXPECT_TRUE(metricProducer2->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
-//    EXPECT_EQ(0, activation1001_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_2->state);
-//    // }}}-----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
-//    EXPECT_EQ(kActive, activation1001_1->state);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_2->state);
-//
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    // }}}--------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1.
-//    auto screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase2 + 200
-//    );
-//    processor2->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
-//    EXPECT_EQ(kActive, activation1001_1->state);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
-//
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
-//    processor2->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor3 =
-//            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor3->mMetricsManagers.size());
-//    it = processor3->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
-//    auto& metricsManagerTimeBase3 = it->second;
-//    EXPECT_TRUE(metricsManagerTimeBase3->isActive());
-//
-//    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase3_1 = *metricIt;
-//    EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
-//
-//    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase3_2 = *metricIt;
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase3_1->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor3->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
-//    EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1 again.
-//    screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase3 + 100 * NS_PER_SEC
-//    );
-//    processor3->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
-//    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk.
-//    shutDownTime = timeBase3 + 500 * NS_PER_SEC;
-//    processor3->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    ttl1 = timeBase3 + ttl1 - shutDownTime;
-//    ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor4 =
-//            CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor4->mMetricsManagers.size());
-//    it = processor4->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor4->mMetricsManagers.end());
-//    auto& metricsManagerTimeBase4 = it->second;
-//    EXPECT_TRUE(metricsManagerTimeBase4->isActive());
-//
-//    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase4_1 = *metricIt;
-//    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
-//
-//    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase4_2 = *metricIt;
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase4_1->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase4_2->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor4->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is not active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
-//    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
-//    int uid = 1111;
-//
-//    // Create config with 2 metrics:
-//    // Metric 1: Activate on boot with 2 activations
-//    // Metric 2: Always active
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor1 =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor1->mMetricsManagers.size());
-//    auto it = processor1->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor1->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer1_1->isActive());  // inactive due to associated MetricActivation
-//
-//    auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer1_2->isActive());
-//
-//    EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation1_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
-//
-//    const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation1_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor1->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1_1->isActive());
-//    EXPECT_EQ(0, activation1_1_1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
-//    EXPECT_EQ(0, activation1_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer1_2->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor1->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1_1->isActive());
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//
-//    EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer2_1->isActive());
-//
-//    auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//
-//    EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation2_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
-//
-//    const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation2_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
-//    // }}}-----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
-//    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_1->state);
-//    EXPECT_EQ(0, activation2_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    // }}}--------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1.
-//    auto screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase2 + 200
-//    );
-//    processor2->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_1->state);
-//    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
-//    processor2->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    ttl1 -= shutDownTime - timeBase2;
-//    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC
-//            - (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor3 =
-//            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor3->mMetricsManagers.size());
-//    it = processor3->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
-//    auto& metricsManager3 = it->second;
-//    EXPECT_TRUE(metricsManager3->isActive());
-//
-//    EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer3_1->isActive());
-//
-//    auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//
-//    EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation3_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation3_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
-//
-//    const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation3_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation3_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor3->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer3_1->isActive());
-//    EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//
-//
-//    // Trigger Activation 2 for Metric 1 again.
-//    screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase3 + 100 * NS_PER_SEC
-//    );
-//    processor3->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
-//    //                  Activation 2 is set to active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer3_1->isActive());
-//    EXPECT_EQ(kNotActive, activation3_1_1->state);
-//    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
-//    int uid = 9876;
-//    long configId = 12341;
-//
-//    // Create config with 3 metrics:
-//    // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
-//    // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
-//    // Metric 3: Always active
-//    StatsdConfig config1;
-//    config1.set_id(configId);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
-//    auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//    *config1.add_atom_matcher() = jobStartMatcher;
-//    *config1.add_atom_matcher() = jobFinishMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    long metricId3 = 1234563;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric3 = config1.add_count_metric();
-//    countMetric3->set_id(metricId3);
-//    countMetric3->set_what(wakelockAcquireMatcher.id());
-//    countMetric3->set_bucket(FIVE_MINUTES);
-//
-//    // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
-//    auto metric2Activation = config1.add_metric_activation();
-//    metric2Activation->set_metric_id(metricId2);
-//    auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
-//    metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
-//    metric2ActivationTrigger1->set_ttl_seconds(100);
-//    metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
-//    metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
-//    metric2ActivationTrigger2->set_ttl_seconds(200);
-//    metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    // Send the config.
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    string serialized = config1.SerializeAsString();
-//    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
-//
-//    // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
-//    StatsdConfig tmpConfig;
-//    ConfigKey cfgKey1(uid, configId);
-//    EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is not active.
-//    // Metric 3 is active.
-//    // {{{---------------------------------------------------------------------------
-//    sp<StatsLogProcessor> processor = service->mProcessor;
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//    EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
-//
-//    auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
-//    EXPECT_EQ(metricId1, metricProducer1->getMetricId());
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
-//    EXPECT_EQ(metricId2, metricProducer2->getMetricId());
-//    EXPECT_FALSE(metricProducer2->isActive());
-//
-//    auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
-//    EXPECT_EQ(metricId3, metricProducer3->getMetricId());
-//    EXPECT_TRUE(metricProducer3->isActive());
-//
-//    // Check event activations.
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
-//              metric1ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
-//              metric1ActivationTrigger2->atom_matcher_id());
-//    const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
-//              metric2ActivationTrigger1->atom_matcher_id());
-//    const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
-//    EXPECT_EQ(0, activation3->start_ns);
-//    EXPECT_EQ(kNotActive, activation3->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
-//              metric2ActivationTrigger2->atom_matcher_id());
-//    const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
-//    EXPECT_EQ(0, activation4->start_ns);
-//    EXPECT_EQ(kNotActive, activation4->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1. Should activate on boot.
-//    // Trigger Activation 4 for Metric 2. Should activate immediately.
-//    long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
-//    processor->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active. Activation 4 set to kActive
-//    // Metric 3 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//
-//    EXPECT_TRUE(metricProducer2->isActive());
-//    EXPECT_EQ(0, activation3->start_ns);
-//    EXPECT_EQ(kNotActive, activation3->state);
-//    EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
-//    EXPECT_EQ(kActive, activation4->state);
-//
-//    EXPECT_TRUE(metricProducer3->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Can't fake time with StatsService.
-//    // Lets get a time close to the system server death time and make sure it's sane.
-//    int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
-//    EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
-//    EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
-//
-//    // System server dies.
-//    service->statsCompanionServiceDiedImpl();
-//
-//    // We should have a new metrics manager. Lets get it and ensure activation status is restored.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//    EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
-//
-//    auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
-//    EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
-//    EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
-//    EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
-//    EXPECT_TRUE(metricProducer1003->isActive());
-//
-//    // Check event activations.
-//    // Activation 1 is kActiveOnBoot.
-//    // Activation 2 and 3 are not active.
-//    // Activation 4 is active.
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
-//              metric1ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
-//    EXPECT_EQ(0, activation1001->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1001->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
-//              metric1ActivationTrigger2->atom_matcher_id());
-//    const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
-//    EXPECT_EQ(0, activation1002->start_ns);
-//    EXPECT_EQ(kNotActive, activation1002->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
-//              metric2ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
-//    EXPECT_EQ(0, activation1003->start_ns);
-//    EXPECT_EQ(kNotActive, activation1003->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
-//              metric2ActivationTrigger2->atom_matcher_id());
-//    const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
-//    EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
-//    EXPECT_EQ(kActive, activation1004->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Clear the data stored on disk as a result of the system server death.
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true,
-//                                ADB_DUMP, FAST, &buffer);
-//}
+TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
+    // Setup a simple config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(123456);
+    countMetric->set_what(wakelockAcquireMatcher.id());
+    countMetric->set_bucket(FIVE_MINUTES);
+
+    ConfigKey cfgKey;
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    vector<uint8_t> bytes;
+    ConfigMetricsReportList output;
+
+    // Dump report WITHOUT erasing data.
+    processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST,
+                            &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    EXPECT_EQ(output.reports_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+    // Dump report WITH erasing data. There should be data since we didn't previously erase it.
+    processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    EXPECT_EQ(output.reports_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+    // Dump report again. There should be no data since we erased it.
+    processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    // We don't care whether statsd has a report, as long as it has no count metrics in it.
+    bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 ||
+                  output.reports(0).metrics(0).count_metrics().data_size() == 0;
+    EXPECT_TRUE(noData);
+}
+
+TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
+    int uid = 1111;
+
+    // Setup a simple config, no activation
+    StatsdConfig config1;
+    int64_t cfgId1 = 12341;
+    config1.set_id(cfgId1);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    ConfigKey cfgKey1(uid, cfgId1);
+
+    // Add another config, with two metrics, one with activation
+    StatsdConfig config2;
+    int64_t cfgId2 = 12342;
+    config2.set_id(cfgId2);
+    config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    *config2.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId3 = 1234561;
+    long metricId4 = 1234562;
+
+    auto countMetric3 = config2.add_count_metric();
+    countMetric3->set_id(metricId3);
+    countMetric3->set_what(wakelockAcquireMatcher.id());
+    countMetric3->set_bucket(FIVE_MINUTES);
+
+    auto countMetric4 = config2.add_count_metric();
+    countMetric4->set_id(metricId4);
+    countMetric4->set_what(wakelockAcquireMatcher.id());
+    countMetric4->set_bucket(FIVE_MINUTES);
+
+    auto metric3Activation = config2.add_metric_activation();
+    metric3Activation->set_metric_id(metricId3);
+    metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric3ActivationTrigger = metric3Activation->add_event_activation();
+    metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric3ActivationTrigger->set_ttl_seconds(100);
+
+    ConfigKey cfgKey2(uid, cfgId2);
+
+    // Add another config, with two metrics, both with activations
+    StatsdConfig config3;
+    int64_t cfgId3 = 12343;
+    config3.set_id(cfgId3);
+    config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    *config3.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId5 = 1234565;
+    long metricId6 = 1234566;
+    auto countMetric5 = config3.add_count_metric();
+    countMetric5->set_id(metricId5);
+    countMetric5->set_what(wakelockAcquireMatcher.id());
+    countMetric5->set_bucket(FIVE_MINUTES);
+
+    auto countMetric6 = config3.add_count_metric();
+    countMetric6->set_id(metricId6);
+    countMetric6->set_what(wakelockAcquireMatcher.id());
+    countMetric6->set_bucket(FIVE_MINUTES);
+
+    auto metric5Activation = config3.add_metric_activation();
+    metric5Activation->set_metric_id(metricId5);
+    metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric5ActivationTrigger = metric5Activation->add_event_activation();
+    metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric5ActivationTrigger->set_ttl_seconds(100);
+
+    auto metric6Activation = config3.add_metric_activation();
+    metric6Activation->set_metric_id(metricId6);
+    metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric6ActivationTrigger = metric6Activation->add_event_activation();
+    metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric6ActivationTrigger->set_ttl_seconds(200);
+
+    ConfigKey cfgKey3(uid, cfgId3);
+
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
+
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(
+            m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1,
+            [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                                                             const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+                                              activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(1, cfgKey1, config1);
+    processor.OnConfigUpdated(2, cfgKey2, config2);
+    processor.OnConfigUpdated(3, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor.mMetricsManagers.size());
+
+    // Expect the first config and both metrics in it to be active.
+    auto it = processor.mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_TRUE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
+    it = processor.mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+
+    metricIt = metricsManager2->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId3) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+    auto& metricProducer3 = *metricIt;
+    EXPECT_FALSE(metricProducer3->isActive());
+
+    metricIt = metricsManager2->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId4) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+    auto& metricProducer4 = *metricIt;
+    EXPECT_TRUE(metricProducer4->isActive());
+
+    // Expect the third config and both metrics in it to be inactive.
+    it = processor.mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager3 = it->second;
+    EXPECT_FALSE(metricsManager3->isActive());
+
+    metricIt = metricsManager3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId5) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+    auto& metricProducer5 = *metricIt;
+    EXPECT_FALSE(metricProducer5->isActive());
+
+    metricIt = metricsManager3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId6) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+    auto& metricProducer6 = *metricIt;
+    EXPECT_FALSE(metricProducer6->isActive());
+
+    // No broadcast for active configs should have happened yet.
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activate all 3 metrics that were not active.
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor.OnLogEvent(event.get());
+
+    // Assert that all 3 configs are active.
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) !=
+                activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) !=
+                activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) !=
+                activeConfigsBroadcast.end());
+
+    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor.SaveActiveConfigsToDisk(shutDownTime);
+    const int64_t ttl3 = event->GetElapsedTimestampNs() +
+                         metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    const int64_t ttl5 = event->GetElapsedTimestampNs() +
+                         metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    const int64_t ttl6 = event->GetElapsedTimestampNs() +
+                         metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+    // Create a second StatsLogProcessor and push the same 3 configs.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+    processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
+    processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor2->mMetricsManagers.size());
+
+    // First config and both metrics are active.
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_TRUE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    // Second config is active. Metric 3 is inactive, metric 4 is active.
+    it = processor2->mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1002 = it->second;
+    EXPECT_TRUE(metricsManager1002->isActive());
+
+    metricIt = metricsManager1002->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId3) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+    auto& metricProducer1003 = *metricIt;
+    EXPECT_FALSE(metricProducer1003->isActive());
+
+    metricIt = metricsManager1002->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId4) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+    auto& metricProducer1004 = *metricIt;
+    EXPECT_TRUE(metricProducer1004->isActive());
+
+    // Config 3 is inactive. both metrics are inactive.
+    it = processor2->mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1003 = it->second;
+    EXPECT_FALSE(metricsManager1003->isActive());
+    EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
+
+    metricIt = metricsManager1003->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId5) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+    auto& metricProducer1005 = *metricIt;
+    EXPECT_FALSE(metricProducer1005->isActive());
+
+    metricIt = metricsManager1003->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId6) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+    auto& metricProducer1006 = *metricIt;
+    EXPECT_FALSE(metricProducer1006->isActive());
+
+    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
+    EXPECT_FALSE(metricProducer1003->isActive());
+    const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+    EXPECT_EQ(0, activation1003->start_ns);
+    EXPECT_FALSE(metricProducer1005->isActive());
+    const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
+    EXPECT_EQ(0, activation1005->start_ns);
+    EXPECT_FALSE(metricProducer1006->isActive());
+    const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
+    EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
+    EXPECT_EQ(0, activation1006->start_ns);
+
+    processor2->LoadActiveConfigsFromDisk();
+
+    // After loading activations from disk, assert that all 3 metrics are active.
+    EXPECT_TRUE(metricProducer1003->isActive());
+    EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
+    EXPECT_TRUE(metricProducer1005->isActive());
+    EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
+    EXPECT_TRUE(metricProducer1006->isActive());
+    EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
+
+    // Make sure no more broadcasts have happened.
+    EXPECT_EQ(broadcastCount, 1);
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBoot) {
+    int uid = 1111;
+
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger = metric1Activation->add_event_activation();
+    metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger->set_ttl_seconds(100);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1->isActive());
+    const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
+
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+    EXPECT_EQ(0, activation1001->start_ns);
+    EXPECT_EQ(kNotActive, activation1001->state);
+
+    processor2->LoadActiveConfigsFromDisk();
+
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
+    EXPECT_EQ(kActive, activation1001->state);
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
+    int uid = 1111;
+
+    // Create config with 2 metrics:
+    // Metric 1: Activate on boot with 2 activations
+    // Metric 2: Always active
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    int i = 0;
+    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+
+    i = 0;
+    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+
+    EXPECT_TRUE(metricProducer2->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1->isActive());
+    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    i = 0;
+    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
+    EXPECT_EQ(0, activation1001_1->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_1->state);
+
+    i = 0;
+    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_2->state);
+    // }}}-----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor2->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+    EXPECT_EQ(kActive, activation1001_1->state);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_2->state);
+
+    EXPECT_TRUE(metricProducer1002->isActive());
+    // }}}--------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1.
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
+    processor2->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+    EXPECT_EQ(kActive, activation1001_1->state);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
+
+    EXPECT_TRUE(metricProducer1002->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+    processor2->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_TRUE(metricProducer1002->isActive());
+    ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor3 =
+            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor3->mMetricsManagers.size());
+    it = processor3->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+    auto& metricsManagerTimeBase3 = it->second;
+    EXPECT_TRUE(metricsManagerTimeBase3->isActive());
+
+    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+    auto& metricProducerTimeBase3_1 = *metricIt;
+    EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
+
+    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+    auto& metricProducerTimeBase3_2 = *metricIt;
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+
+    i = 0;
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase3_1->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+
+    i = 0;
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor3->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+    EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1 again.
+    screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor3->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk.
+    shutDownTime = timeBase3 + 500 * NS_PER_SEC;
+    processor3->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_TRUE(metricProducer1002->isActive());
+    ttl1 = timeBase3 + ttl1 - shutDownTime;
+    ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor4 =
+            CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor4->mMetricsManagers.size());
+    it = processor4->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor4->mMetricsManagers.end());
+    auto& metricsManagerTimeBase4 = it->second;
+    EXPECT_TRUE(metricsManagerTimeBase4->isActive());
+
+    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+    auto& metricProducerTimeBase4_1 = *metricIt;
+    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+
+    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+    auto& metricProducerTimeBase4_2 = *metricIt;
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+
+    i = 0;
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase4_1->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+
+    i = 0;
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase4_2->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor4->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is not active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
+    int uid = 1111;
+
+    // Create config with 2 metrics:
+    // Metric 1: Activate on boot with 2 activations
+    // Metric 2: Always active
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor1 =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor1->mMetricsManagers.size());
+    auto it = processor1->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor1->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer1_1->isActive());  // inactive due to associated MetricActivation
+
+    auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer1_2->isActive());
+
+    EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
+    EXPECT_EQ(0, activation1_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
+
+    const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
+    EXPECT_EQ(0, activation1_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor1->OnLogEvent(event.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1_1->isActive());
+    EXPECT_EQ(0, activation1_1_1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
+    EXPECT_EQ(0, activation1_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_2->state);
+
+    EXPECT_TRUE(metricProducer1_2->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor1->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1_1->isActive());
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+
+    EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer2_1->isActive());
+
+    auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer2_2->isActive());
+
+    EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
+    EXPECT_EQ(0, activation2_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
+
+    const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
+    EXPECT_EQ(0, activation2_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
+    // }}}-----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor2->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+    EXPECT_EQ(kActive, activation2_1_1->state);
+    EXPECT_EQ(0, activation2_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_2->state);
+
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    // }}}--------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1.
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
+    processor2->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+    EXPECT_EQ(kActive, activation2_1_1->state);
+    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
+    EXPECT_EQ(kActive, activation2_1_2->state);
+
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+    processor2->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    ttl1 -= shutDownTime - timeBase2;
+    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC -
+                   (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor3 =
+            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor3->mMetricsManagers.size());
+    it = processor3->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+    auto& metricsManager3 = it->second;
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer3_1->isActive());
+
+    auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer3_2->isActive());
+
+    EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
+    EXPECT_EQ(0, activation3_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation3_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
+
+    const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
+    EXPECT_EQ(0, activation3_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation3_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor3->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer3_1->isActive());
+    EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
+    EXPECT_EQ(kActive, activation3_1_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
+    EXPECT_EQ(kActive, activation3_1_2->state);
+
+    EXPECT_TRUE(metricProducer3_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1 again.
+    screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor3->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
+    //                  Activation 2 is set to active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer3_1->isActive());
+    EXPECT_EQ(kNotActive, activation3_1_1->state);
+    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
+    EXPECT_EQ(kActive, activation3_1_2->state);
+
+    EXPECT_TRUE(metricProducer3_2->isActive());
+    // }}}---------------------------------------------------------------------------
+}
+
+TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
+    int uid = 9876;
+    long configId = 12341;
+
+    // Create config with 3 metrics:
+    // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 3: Always active
+    StatsdConfig config1;
+    config1.set_id(configId);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
+    auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+    *config1.add_atom_matcher() = jobStartMatcher;
+    *config1.add_atom_matcher() = jobFinishMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    long metricId3 = 1234563;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto countMetric3 = config1.add_count_metric();
+    countMetric3->set_id(metricId3);
+    countMetric3->set_what(wakelockAcquireMatcher.id());
+    countMetric3->set_bucket(FIVE_MINUTES);
+
+    // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
+    auto metric2Activation = config1.add_metric_activation();
+    metric2Activation->set_metric_id(metricId2);
+    auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
+    metric2ActivationTrigger1->set_ttl_seconds(100);
+    metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
+    metric2ActivationTrigger2->set_ttl_seconds(200);
+    metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Send the config.
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    string serialized = config1.SerializeAsString();
+    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+
+    // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
+    StatsdConfig tmpConfig;
+    ConfigKey cfgKey1(uid, configId);
+    EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
+
+    // Metric 1 is not active.
+    // Metric 2 is not active.
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    sp<StatsLogProcessor> processor = service->mProcessor;
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
+
+    auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1->getMetricId());
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer2->getMetricId());
+    EXPECT_FALSE(metricProducer2->isActive());
+
+    auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer3->getMetricId());
+    EXPECT_TRUE(metricProducer3->isActive());
+
+    // Check event activations.
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
+    EXPECT_EQ(0, activation4->start_ns);
+    EXPECT_EQ(kNotActive, activation4->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1. Should activate on boot.
+    // Trigger Activation 4 for Metric 2. Should activate immediately.
+    long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(
+            1 + configAddedTimeNs, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event1.get());
+
+    std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent(
+            2 + configAddedTimeNs, attributionUids, attributionTags, "finish1");
+    processor->OnLogEvent(event2.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active. Activation 4 set to kActive
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+
+    EXPECT_TRUE(metricProducer2->isActive());
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
+    EXPECT_EQ(kActive, activation4->state);
+
+    EXPECT_TRUE(metricProducer3->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Can't fake time with StatsService.
+    // Lets get a time close to the system server death time and make sure it's sane.
+    int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
+    EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
+    EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
+
+    // System server dies.
+    service->statsCompanionServiceDiedImpl();
+
+    // We should have a new metrics manager. Lets get it and ensure activation status is restored.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
+
+    auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
+    EXPECT_TRUE(metricProducer1003->isActive());
+
+    // Check event activations.
+    // Activation 1 is kActiveOnBoot.
+    // Activation 2 and 3 are not active.
+    // Activation 4 is active.
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+    EXPECT_EQ(0, activation1001->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1001->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
+    EXPECT_EQ(0, activation1002->start_ns);
+    EXPECT_EQ(kNotActive, activation1002->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+    EXPECT_EQ(0, activation1003->start_ns);
+    EXPECT_EQ(kNotActive, activation1003->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
+    EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
+    EXPECT_EQ(kActive, activation1004->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Clear the data stored on disk as a result of the system server death.
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST,
+                            &buffer);
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index a49c18f..29005a2 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -39,35 +39,28 @@
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(UidMapTest, TestIsolatedUID) {
-//    sp<UidMap> m = new UidMap();
-//    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-//    sp<AlarmMonitor> anomalyAlarmMonitor;
-//    sp<AlarmMonitor> subscriberAlarmMonitor;
-//    // Construct the processor with a dummy sendBroadcast function that does nothing.
-//    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-//                        [](const ConfigKey& key) { return true; },
-//                        [](const int&, const vector<int64_t>&) {return true;});
-//    LogEvent addEvent(util::ISOLATED_UID_CHANGED, 1);
-//    addEvent.write(100);  // parent UID
-//    addEvent.write(101);  // isolated UID
-//    addEvent.write(1);    // Indicates creation.
-//    addEvent.init();
-//
-//    EXPECT_EQ(101, m->getHostUidOrSelf(101));
-//
-//    p.OnLogEvent(&addEvent);
-//    EXPECT_EQ(100, m->getHostUidOrSelf(101));
-//
-//    LogEvent removeEvent(util::ISOLATED_UID_CHANGED, 1);
-//    removeEvent.write(100);  // parent UID
-//    removeEvent.write(101);  // isolated UID
-//    removeEvent.write(0);    // Indicates removal.
-//    removeEvent.init();
-//    p.OnLogEvent(&removeEvent);
-//    EXPECT_EQ(101, m->getHostUidOrSelf(101));
-//}
+TEST(UidMapTest, TestIsolatedUID) {
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    // Construct the processor with a dummy sendBroadcast function that does nothing.
+    StatsLogProcessor p(
+            m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+            [](const ConfigKey& key) { return true; },
+            [](const int&, const vector<int64_t>&) { return true; });
+
+    std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent(
+            1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/);
+    EXPECT_EQ(101, m->getHostUidOrSelf(101));
+    p.OnLogEvent(addEvent.get());
+    EXPECT_EQ(100, m->getHostUidOrSelf(101));
+
+    std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent(
+            1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/);
+    p.OnLogEvent(removeEvent.get());
+    EXPECT_EQ(101, m->getHostUidOrSelf(101));
+}
 
 TEST(UidMapTest, TestMatching) {
     UidMap m;
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 4c55683..5eef92e 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -12,18 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gtest/gtest.h>
+#include "src/shell/ShellSubscriber.h"
 
+#include <gtest/gtest.h>
+#include <stdio.h>
 #include <unistd.h>
+
+#include <vector>
+
 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
-#include "src/shell/ShellSubscriber.h"
 #include "stats_event.h"
 #include "tests/metrics/metrics_test_helper.h"
-
-#include <stdio.h>
-#include <vector>
+#include "tests/statsd_test_util.h"
 
 using namespace android::os::statsd;
 using android::sp;
@@ -118,18 +120,9 @@
     vector<std::shared_ptr<LogEvent>> pushedList;
 
     // Create the LogEvent from an AStatsEvent
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, 29 /*screen_state_atom_id*/);
-    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
-    AStatsEvent_writeInt32(statsEvent, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    AStatsEvent_build(statsEvent);
-    size_t size;
-    uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &size);
-    std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
-    logEvent->parseBuffer(buffer, size);
-    AStatsEvent_release(statsEvent);
-
-    pushedList.push_back(logEvent);
+    std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent(
+            1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    pushedList.push_back(std::move(logEvent));
 
     // create a simple config to get screen events
     ShellSubscription config;
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index b1633c6..a0e0095 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <gtest/gtest.h>
-#include "state/StateManager.h"
 #include "state/StateTracker.h"
-#include "state/StateListener.h"
 
+#include <gtest/gtest.h>
+
+#include "state/StateListener.h"
+#include "state/StateManager.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
@@ -26,6 +28,8 @@
 namespace os {
 namespace statsd {
 
+const int32_t timestampNs = 1000;
+
 /**
  * Mock StateListener class for testing.
  * Stores primary key and state pairs.
@@ -56,95 +60,49 @@
     return output.mValue.int_value;
 }
 
-// TODO(b/149590301): Update these helpers to use new socket schema.
-//// START: build event functions.
-//// State with no primary fields - ScreenStateChanged
-//std::shared_ptr<LogEvent> buildScreenEvent(int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// State with one primary field - UidProcessStateChanged
-//std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// State with first uid in attribution chain as primary field - WakelockStateChanged
-//std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) {
-//    std::vector<AttributionNodeInternal> chain;
-//    chain.push_back(AttributionNodeInternal());
-//    AttributionNodeInternal& attr = chain.back();
-//    attr.set_uid(uid);
-//
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */);
-//    event->write(chain);
-//    event->write((int32_t)1);  // PARTIAL_WAKE_LOCK
-//    event->write(tag);
-//    event->write(acquire ? 1 : 0);
-//    event->init();
-//    return event;
-//}
-//
-//// State with multiple primary fields - OverlayStateChanged
-//std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write(true);  // using_alert_window
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// Incorrect event - missing fields
-//std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// Incorrect event - exclusive state has wrong type
-//std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write(true);
-//    event->write("string");  // exclusive state: string instead of int
-//    event->init();
-//    return event;
-//}
-//
-//std::shared_ptr<LogEvent> buildBleScanEvent(int uid, bool acquire, bool reset) {
-//    std::vector<AttributionNodeInternal> chain;
-//    chain.push_back(AttributionNodeInternal());
-//    AttributionNodeInternal& attr = chain.back();
-//    attr.set_uid(uid);
-//
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::BLE_SCAN_STATE_CHANGED, 1000);
-//    event->write(chain);
-//    event->write(reset ? 2 : acquire ? 1 : 0);  // PARTIAL_WAKE_LOCK
-//    event->write(0);                            // filtered
-//    event->write(0);                            // first match
-//    event->write(0);                            // opportunistic
-//    event->init();
-//    return event;
-//}
+// START: build event functions.
+// Incorrect event - missing fields
+std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
+                                                     int state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    // Missing field 3 - using_alert_window.
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+// Incorrect event - exclusive state has wrong type
+std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    AStatsEvent_writeInt32(statsEvent, true);       // using_alert_window
+    AStatsEvent_writeString(statsEvent, "string");  // exclusive state: string instead of int
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
 // END: build event functions.
 
 // START: get primary key functions
@@ -293,302 +251,323 @@
     EXPECT_EQ(0, mgr.getStateTrackersCount());
     EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
 }
-// TODO(b/149590301): Update these tests to use new socket schema.
-///**
-// * Test a binary state atom with nested counting.
-// *
-// * To go from an "ON" state to an "OFF" state with nested counting, we must see
-// * an equal number of "OFF" events as "ON" events.
-// * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
-// * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
-// */
-//TEST(StateTrackerTest, TestStateChangeNested) {
-//    sp<TestStateListener> listener = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
-//
-//    std::shared_ptr<LogEvent> event1 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event2 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(0, listener->updates.size());
-//
-//    std::shared_ptr<LogEvent> event3 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
-//    mgr.onLogEvent(*event3);
-//    EXPECT_EQ(0, listener->updates.size());
-//
-//    std::shared_ptr<LogEvent> event4 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
-//    mgr.onLogEvent(*event4);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(0, listener->updates[0].mState);
-//}
-//
-///**
-// * Test a state atom with a reset state.
-// *
-// * If the reset state value is seen, every state in the map is set to the default
-// * state and every listener is notified.
-// */
-//TEST(StateTrackerTest, TestStateChangeReset) {
-//    sp<TestStateListener> listener = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
-//
-//    std::shared_ptr<LogEvent> event1 =
-//            buildBleScanEvent(1000 /* uid */, true /*acquire*/, false /*reset*/);
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event2 =
-//            buildBleScanEvent(2000 /* uid */, true /*acquire*/, false /*reset*/);
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event3 =
-//            buildBleScanEvent(2000 /* uid */, false /*acquire*/, true /*reset*/);
-//    mgr.onLogEvent(*event3);
-//    EXPECT_EQ(2, listener->updates.size());
-//    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
-//    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states without primary keys.
-// */
-//TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
-//    EXPECT_EQ(2, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//              getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states with one primary key.
-// */
-//TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1002, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey;
-//    getUidProcessKey(1000 /* uid */, &queryKey);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
-//}
-//
-//TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
-//
-//    // Log event.
-//    std::shared_ptr<LogEvent> event =
-//            buildPartialWakelockEvent(1001 /* uid */, "tag1", true /* acquire */);
-//    mgr.onLogEvent(*event);
-//
-//    EXPECT_EQ(1, mgr.getStateTrackersCount());
-//    EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
-//
-//    // Check listener was updated.
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
-//    EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
-//    EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
-//
-//    // Check StateTracker was updated by querying for state.
-//    HashableDimensionKey queryKey;
-//    getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
-//
-//    // No state stored for this query key.
-//    HashableDimensionKey queryKey2;
-//    getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
-//
-//    // Partial query fails.
-//    HashableDimensionKey queryKey3;
-//    getPartialWakelockKey(1001 /* uid */, &queryKey3);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states with multiple primary keys.
-// */
-//TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildOverlayEvent(1000 /* uid */, "package1", 1);  // state: ENTERED
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey;
-//    getOverlayKey(1000 /* uid */, "package1", &queryKey);
-//    EXPECT_EQ(OverlayStateChanged::ENTERED,
-//              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged
-// * when there is an error extracting state from log event. Listener is not
-// * updated of state change.
-// */
-//TEST(StateTrackerTest, TestStateChangeEventError) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event1 =
-//            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
-//    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
-//
-//    // check listener was updated
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(0, listener1->updates.size());
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(0, listener1->updates.size());
-//}
-//
-//TEST(StateTrackerTest, TestStateQuery) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    sp<TestStateListener> listener2 = new TestStateListener();
-//    sp<TestStateListener> listener3 = new TestStateListener();
-//    sp<TestStateListener> listener4 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-//    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
-//
-//    std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
-//            1000,
-//            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
-//    std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
-//            1001,
-//            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
-//                                                                                //  1003
-//    std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
-//            1002,
-//            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
-//    std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
-//            1001,
-//            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
-//    std::shared_ptr<LogEvent> event5 =
-//            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-//    std::shared_ptr<LogEvent> event6 =
-//            buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED);
-//    std::shared_ptr<LogEvent> event7 =
-//            buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED);
-//    std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true);
-//    std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false);
-//
-//    mgr.onLogEvent(*event1);
-//    mgr.onLogEvent(*event2);
-//    mgr.onLogEvent(*event3);
-//    mgr.onLogEvent(*event5);
-//    mgr.onLogEvent(*event5);
-//    mgr.onLogEvent(*event6);
-//    mgr.onLogEvent(*event7);
-//    mgr.onLogEvent(*event8);
-//    mgr.onLogEvent(*event9);
-//
-//    // Query for UidProcessState of uid 1001
-//    HashableDimensionKey queryKey1;
-//    getUidProcessKey(1001, &queryKey1);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-//
-//    // Query for UidProcessState of uid 1004 - not in state map
-//    HashableDimensionKey queryKey2;
-//    getUidProcessKey(1004, &queryKey2);
-//    EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
-//                              queryKey2));  // default state
-//
-//    // Query for UidProcessState of uid 1001 - after change in state
-//    mgr.onLogEvent(*event4);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-//
-//    // Query for ScreenState
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//              getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
-//
-//    // Query for OverlayState of uid 1000, package name "package2"
-//    HashableDimensionKey queryKey3;
-//    getOverlayKey(1000, "package2", &queryKey3);
-//    EXPECT_EQ(OverlayStateChanged::EXITED,
-//              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
-//
-//    // Query for WakelockState of uid 1005, tag 2
-//    HashableDimensionKey queryKey4;
-//    getPartialWakelockKey(1005, "tag2", &queryKey4);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
-//
-//    // Query for WakelockState of uid 1005, tag 1
-//    HashableDimensionKey queryKey5;
-//    getPartialWakelockKey(1005, "tag1", &queryKey5);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
-//}
+
+/**
+ * Test a binary state atom with nested counting.
+ *
+ * To go from an "ON" state to an "OFF" state with nested counting, we must see
+ * an equal number of "OFF" events as "ON" events.
+ * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
+ * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
+ */
+TEST(StateTrackerTest, TestStateChangeNested) {
+    sp<TestStateListener> listener = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
+
+    std::vector<int> attributionUids1 = {1000};
+    std::vector<string> attributionTags1 = {"tag"};
+
+    std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1,
+                                                                  attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent(
+            timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(0, listener->updates.size());
+
+    std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent(
+            timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event3);
+    EXPECT_EQ(0, listener->updates.size());
+
+    std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent(
+            timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event4);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0, listener->updates[0].mState);
+}
+
+/**
+ * Test a state atom with a reset state.
+ *
+ * If the reset state value is seen, every state in the map is set to the default
+ * state and every listener is notified.
+ */
+TEST(StateTrackerTest, TestStateChangeReset) {
+    sp<TestStateListener> listener = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
+
+    std::vector<int> attributionUids1 = {1000};
+    std::vector<string> attributionTags1 = {"tag1"};
+    std::vector<int> attributionUids2 = {2000};
+
+    std::unique_ptr<LogEvent> event1 =
+            CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1,
+                                           BleScanStateChanged::ON, false, false, false);
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event2 =
+            CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1,
+                                           BleScanStateChanged::ON, false, false, false);
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event3 =
+            CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1,
+                                           BleScanStateChanged::RESET, false, false, false);
+    mgr.onLogEvent(*event3);
+    EXPECT_EQ(2, listener->updates.size());
+    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
+    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
+            timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+    EXPECT_EQ(2, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with one primary key.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
+            timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1002, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getUidProcessKey(1000 /* uid */, &queryKey);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
+
+    // Log event.
+    std::vector<int> attributionUids = {1001};
+    std::vector<string> attributionTags = {"tag1"};
+
+    std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids,
+                                                                 attributionTags, "wakelockName");
+    mgr.onLogEvent(*event);
+    EXPECT_EQ(1, mgr.getStateTrackersCount());
+    EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
+
+    // Check listener was updated.
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
+    EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
+    EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
+
+    // Check StateTracker was updated by querying for state.
+    HashableDimensionKey queryKey;
+    getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
+
+    // No state stored for this query key.
+    HashableDimensionKey queryKey2;
+    getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
+
+    // Partial query fails.
+    HashableDimensionKey queryKey3;
+    getPartialWakelockKey(1001 /* uid */, &queryKey3);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent(
+            timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/,
+            OverlayStateChanged::ENTERED);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getOverlayKey(1000 /* uid */, "package1", &queryKey);
+    EXPECT_EQ(OverlayStateChanged::ENTERED,
+              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+    // log event
+    std::shared_ptr<LogEvent> event1 =
+            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
+
+    // check listener was updated
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(0, listener1->updates.size());
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    sp<TestStateListener> listener2 = new TestStateListener();
+    sp<TestStateListener> listener3 = new TestStateListener();
+    sp<TestStateListener> listener4 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
+
+    std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent(
+            timestampNs, 1000 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent(
+            timestampNs + 1000, 1001 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
+                                                                                //  1003
+    std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent(
+            timestampNs + 2000, 1002 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
+    std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent(
+            timestampNs + 3000, 1001 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent(
+            timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent(
+            timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/,
+            OverlayStateChanged::ENTERED);
+    std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent(
+            timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/,
+            OverlayStateChanged::EXITED);
+
+    std::vector<int> attributionUids = {1005};
+    std::vector<string> attributionTags = {"tag"};
+
+    std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent(
+            timestampNs + 7000, attributionUids, attributionTags, "wakelock1");
+    std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent(
+            timestampNs + 8000, attributionUids, attributionTags, "wakelock2");
+
+    mgr.onLogEvent(*event1);
+    mgr.onLogEvent(*event2);
+    mgr.onLogEvent(*event3);
+    mgr.onLogEvent(*event5);
+    mgr.onLogEvent(*event5);
+    mgr.onLogEvent(*event6);
+    mgr.onLogEvent(*event7);
+    mgr.onLogEvent(*event8);
+    mgr.onLogEvent(*event9);
+
+    // Query for UidProcessState of uid 1001
+    HashableDimensionKey queryKey1;
+    getUidProcessKey(1001, &queryKey1);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+    // Query for UidProcessState of uid 1004 - not in state map
+    HashableDimensionKey queryKey2;
+    getUidProcessKey(1004, &queryKey2);
+    EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
+                              queryKey2));  // default state
+
+    // Query for UidProcessState of uid 1001 - after change in state
+    mgr.onLogEvent(*event4);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+    // Query for ScreenState
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+    // Query for OverlayState of uid 1000, package name "package2"
+    HashableDimensionKey queryKey3;
+    getOverlayKey(1000, "package2", &queryKey3);
+    EXPECT_EQ(OverlayStateChanged::EXITED,
+              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
+
+    // Query for WakelockState of uid 1005, tag 2
+    HashableDimensionKey queryKey4;
+    getPartialWakelockKey(1005, "wakelock2", &queryKey4);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
+
+    // Query for WakelockState of uid 1005, tag 1
+    HashableDimensionKey queryKey5;
+    getPartialWakelockKey(1005, "wakelock1", &queryKey5);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 050dbf8..c7838fc 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -600,32 +600,51 @@
     return logEvent;
 }
 
-//std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
-//        const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
-//        const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
-//    event->write(attributions);
-//    event->write(jobName);
-//    event->write(state);
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-//    const std::vector<AttributionNodeInternal>& attributions,
-//    const string& name, uint64_t timestampNs) {
-//    return CreateScheduledJobStateChangedEvent(
-//            attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
-//}
-//
-//// Create log event when scheduled job finishes.
-//std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-//    const std::vector<AttributionNodeInternal>& attributions,
-//    const string& name, uint64_t timestampNs) {
-//    return CreateScheduledJobStateChangedEvent(
-//            attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
-//}
-//
+std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
+        const vector<int>& attributionUids, const vector<string>& attributionTags,
+        const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, jobName.c_str());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::STARTED, timestampNs);
+}
+
+// Create log event when scheduled job finishes.
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::FINISHED, timestampNs);
+}
+
 std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
                                                           const vector<int>& attributionUids,
                                                           const vector<string>& attributionTags,
@@ -833,6 +852,62 @@
     return logEvent;
 }
 
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+                                                         const vector<int>& attributionUids,
+                                                         const vector<string>& attributionTags,
+                                                         const BleScanStateChanged::State state,
+                                                         const bool filtered, const bool firstMatch,
+                                                         const bool opportunistic) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_writeInt32(statsEvent, filtered);       // filtered
+    AStatsEvent_writeInt32(statsEvent, firstMatch);     // first match
+    AStatsEvent_writeInt32(statsEvent, opportunistic);  // opportunistic
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+                                                         const string& packageName,
+                                                         const bool usingAlertWindow,
+                                                         const OverlayStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
                                               const shared_ptr<IPullAtomCallback>& puller,
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ead041c..05e1572 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -195,14 +195,16 @@
 std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level);
 
 // Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName);
 
 // Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName);
 
 // Create log event when battery saver starts.
 std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
@@ -247,6 +249,18 @@
 std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
         uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
 
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+                                                         const vector<int>& attributionUids,
+                                                         const vector<string>& attributionTags,
+                                                         const BleScanStateChanged::State state,
+                                                         const bool filtered, const bool firstMatch,
+                                                         const bool opportunistic);
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+                                                         const string& packageName,
+                                                         const bool usingAlertWindow,
+                                                         const OverlayStateChanged::State state);
+
 // Helper function to create an AttributionNodeInternal proto.
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a62f0a6..bd3fee2d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -201,6 +201,7 @@
 import java.net.InetAddress;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -7413,8 +7414,10 @@
                 if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/")) {
                     Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath);
                     try {
-                        Files.move(new File(oldPath).toPath(), new File(newPath).toPath());
+                        Files.move(new File(oldPath).toPath(), new File(newPath).toPath(),
+                                StandardCopyOption.REPLACE_EXISTING);
                     } catch (IOException e2) {
+                        Log.e(TAG, "Rename recovery failed ", e);
                         throw e;
                     }
                 } else {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 948546b..78d3581 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@
     void clearData(String pkg, int uid, boolean fromApp);
     void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
     void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
-    // TODO(b/144152069): Remove this after assessing impact on dogfood.
-    void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
     void cancelToast(String pkg, IBinder token);
     void finishToken(String pkg, IBinder token);
 
@@ -123,10 +121,14 @@
     // INotificationListener method.
     @UnsupportedAppUsage
     StatusBarNotification[] getActiveNotifications(String callingPkg);
+    StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+            String callingAttributionTag);
     @UnsupportedAppUsage
     StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed);
+    StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+            String callingAttributionTag, int count, boolean includeSnoozed);
 
-    NotificationHistory getNotificationHistory(String callingPkg);
+    NotificationHistory getNotificationHistory(String callingPkg, String callingAttributionTag);
 
     void registerListener(in INotificationListener listener, in ComponentName component, int userid);
     void unregisterListener(in INotificationListener listener, int userid);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 811b9c0..0e97e3f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Build;
@@ -841,7 +842,8 @@
     }
 
     /**
-     * Returns the notification channel settings for a given channel and conversation id.
+     * Returns the notification channel settings for a given channel and
+     * {@link ShortcutInfo#getId() conversation id}.
      *
      * <p>The channel must belong to your package, or to a package you are an approved notification
      * delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 51cfa31..c4458b3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2429,7 +2429,7 @@
             PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PersonalAppSuspensionReason {}
+    public @interface PersonalAppsSuspensionReason {}
 
     /**
      * Return true if the given administrator component is currently active (enabled) in the system.
@@ -11961,7 +11961,7 @@
      *     {@link #PERSONAL_APPS_NOT_SUSPENDED} if apps are not suspended.
      * @see #setPersonalAppsSuspended
      */
-    public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(
+    public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(
             @NonNull ComponentName admin) {
         throwIfParentInstance("getPersonalAppsSuspendedReasons");
         if (mService != null) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 95385ee..b1d6c83 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@
                 && hasWebURI();
     }
 
+    private boolean isImageCaptureIntent() {
+        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+                || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+    }
+
+    /** @hide */
+    public boolean isImplicitImageCaptureIntent() {
+        return mPackage == null && mComponent == null && isImageCaptureIntent();
+    }
+
     /**
      * @hide
      */
@@ -11241,9 +11252,7 @@
                 }
                 putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
             }
-        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
-                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
-                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+        } else if (isImageCaptureIntent()) {
             final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
             if (output != null) {
                 putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
@@ -11349,9 +11358,7 @@
                 }
             } catch (ClassCastException e) {
             }
-        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
-                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
-                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+        } else if (isImageCaptureIntent()) {
             final Uri output;
             try {
                 output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 179fc5c..6ba811e 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -268,15 +268,17 @@
     }
 
     /**
-     * Returns whether the calling package can request user consent to interact across profiles.
+     * Returns whether the calling package can request to navigate the user to
+     * the relevant settings page to request user consent to interact across profiles.
      *
-     * <p>If {@code true}, user consent can be obtained via {@link
+     * <p>If {@code true}, the navigation intent can be obtained via {@link
      * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
      * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
      *
      * <p>Specifically, returns whether the following are all true:
      * <ul>
-     * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
+     * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for
+     * the calling user.</li>
      * <li>The calling app has requested</li>
      * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.
      * <li>The calling package has either been whitelisted by default by the OEM or has been
@@ -285,6 +287,10 @@
      * </li>
      * </ul>
      *
+     * <p>Note that in order for the user to be able to grant the consent, the requesting package
+     * must be whitelisted by the admin or the OEM and installed in the other profile. If this is
+     * not the case the user will be shown a message explaining why they can't grant the consent.
+     *
      * <p>Note that user consent could already be granted if given a return value of {@code true}.
      * The package's current ability to interact across profiles can be checked with {@link
      * #canInteractAcrossProfiles()}.
@@ -422,6 +428,23 @@
     }
 
     /**
+     * Returns {@code true} if the given package has requested
+     * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one
+     * other profile in the same profile group.
+     *
+     * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
+     * not return {@code false} if the app is not whitelisted or not installed in the other profile.
+     *
+     * @hide
+     */
+    public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+        try {
+            return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+    /**
      * For each of the packages defined in {@code previousCrossProfilePackages} but not included in
      * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
      * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 4cecb30..9b0dae2 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -38,5 +38,6 @@
     boolean canRequestInteractAcrossProfiles(in String callingPackage);
     void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
     boolean canConfigureInteractAcrossProfiles(in String packageName);
+    boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName);
     void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f151cf..370469e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7088,7 +7088,7 @@
      * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
      * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
      * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
-     * packages to keep the device in a functioning state, e.g. the default dialer.
+     * packages to keep the device in a functioning state, e.g. the default dialer and launcher.
      * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
      *
      * <p>
@@ -7106,7 +7106,7 @@
     @RequiresPermission(Manifest.permission.SUSPEND_APPS)
     @NonNull
     public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
-        throw new UnsupportedOperationException("canSuspendPackages not implemented");
+        throw new UnsupportedOperationException("getUnsuspendablePackages not implemented");
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 34a40ae..eb6901f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1214,7 +1214,7 @@
      * <p>If the camera device supports zoom-out from 1x zoom, minZoom will be less than 1.0, and
      * setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values less than 1.0 increases the camera's field
      * of view.</p>
-     * <p><b>Units</b>: A pair of zoom ratio in floating points: (minZoom, maxZoom)</p>
+     * <p><b>Units</b>: A pair of zoom ratio in floating-points: (minZoom, maxZoom)</p>
      * <p><b>Range of valid values:</b><br></p>
      * <p>maxZoom &gt;= 1.0 &gt;= minZoom</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0ee7482..6905f83 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2180,27 +2180,66 @@
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
      * crop to achieve aspect ratios different than the native camera sensor.</p>
      * <p>By using this control, the application gains a simpler way to control zoom, which can
-     * be a combination of optical and digital zoom. More specifically, for a logical
-     * multi-camera with more than one focal length, using a floating point zoom ratio offers
-     * more zoom precision when a telephoto lens is used, as well as allowing zoom ratio of
-     * less than 1.0 to zoom out to a wide field of view.</p>
-     * <p>Note that the coordinate system of cropRegion, AE/AWB/AF regions, and faces now changes
-     * to the effective after-zoom field-of-view represented by rectangle of (0, 0,
-     * activeArrayWidth, activeArrayHeight).</p>
-     * <p>For example, if {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is 4032*3024, and the preview stream
-     * is configured to the same 4:3 aspect ratio, the application can achieve 2.0x zoom in
-     * one of two ways:</p>
+     * be a combination of optical and digital zoom. For example, a multi-camera system may
+     * contain more than one lens with different focal lengths, and the user can use optical
+     * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
+     * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+     *   better precision compared to an integer value of {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.
+     * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+     *   {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} doesn't.</p>
+     * <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
+     * and output streams, for a hypothetical camera device with an active array of size
+     * <code>(2000,1500)</code>.</p>
      * <ul>
-     * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 4032, 3024)</li>
-     * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (1008, 756, 3024, 2268)</li>
+     * <li>Camera Configuration:<ul>
+     * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+     * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+     * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
      * </ul>
-     * <p>If the application intends to set aeRegions to be top-left quarter of the preview
-     * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 2016, 1512) with
+     * </li>
+     * <li>Case #1: 4:3 crop region with 2.0x zoom ratio<ul>
+     * <li>Zoomed field of view: 1/4 of original field of view</li>
+     * <li>Crop region: <code>Rect(0, 0, 2000, 1500) // (left, top, right, bottom)</code> (post zoom)</li>
+     * </ul>
+     * </li>
+     * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png" /><ul>
+     * <li><code>640x480</code> stream source area: <code>(0, 0, 2000, 1500)</code> (equal to crop region)</li>
+     * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #2: 16:9 crop region with 2.0x zoom.<ul>
+     * <li>Zoomed field of view: 1/4 of original field of view</li>
+     * <li>Crop region: <code>Rect(0, 187, 2000, 1312)</code></li>
+     * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (equal to crop region)</li>
+     * </ul>
+     * </li>
+     * <li>Case #3: 1:1 crop region with 0.5x zoom out to ultrawide lens.<ul>
+     * <li>Zoomed field of view: 4x of original field of view (switched from wide lens to ultrawide lens)</li>
+     * <li>Crop region: <code>Rect(250, 0, 1750, 1500)</code></li>
+     * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (letterboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(250, 328, 1750, 1172)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * </ul>
+     * <p>As seen from the graphs above, the coordinate system of cropRegion now changes to the
+     * effective after-zoom field-of-view, and is represented by the rectangle of (0, 0,
+     * activeArrayWith, activeArrayHeight). The same applies to AE/AWB/AF regions, and faces.
+     * This coordinate system change isn't applicable to RAW capture and its related
+     * metadata such as intrinsicCalibration and lensShadingMap.</p>
+     * <p>Using the same hypothetical example above, and assuming output stream #1 (640x480) is
+     * the viewfinder stream, the application can achieve 2.0x zoom in one of two ways:</p>
+     * <ul>
+     * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 2000, 1500)</li>
+     * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (500, 375, 1500, 1125)</li>
+     * </ul>
+     * <p>If the application intends to set aeRegions to be top-left quarter of the viewfinder
+     * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 1000, 750) with
      * zoomRatio set to 2.0. Alternatively, the application can set aeRegions to the equivalent
-     * region of (1008, 756, 2016, 1512) for zoomRatio of 1.0. If the application doesn't
+     * region of (500, 375, 1000, 750) for zoomRatio of 1.0. If the application doesn't
      * explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
-     * <p>This coordinate system change isn't applicable to RAW capture and its related metadata
-     * such as intrinsicCalibration and lensShadingMap.</p>
      * <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
      * must only be used for letterboxing or pillarboxing of the sensor active array, and no
      * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
@@ -2216,7 +2255,6 @@
      * @see CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CaptureRequest#SCALER_CROP_REGION
-     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     @NonNull
@@ -2574,12 +2612,15 @@
      * frames before the lens can change to the requested focal length.
      * While the focal length is still changing, {@link CaptureResult#LENS_STATE android.lens.state} will
      * be set to MOVING.</p>
-     * <p>Optical zoom will not be supported on most devices.</p>
+     * <p>Optical zoom via this control will not be supported on most devices. Starting from API
+     * level 30, the camera device may combine optical and digital zoom through the
+     * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} control.</p>
      * <p><b>Units</b>: Millimeters</p>
      * <p><b>Range of valid values:</b><br>
      * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS android.lens.info.availableFocalLengths}</p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_ZOOM_RATIO
      * @see CaptureRequest#LENS_APERTURE
      * @see CaptureRequest#LENS_FOCUS_DISTANCE
      * @see CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 096aa0c..be03502 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2410,27 +2410,66 @@
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
      * crop to achieve aspect ratios different than the native camera sensor.</p>
      * <p>By using this control, the application gains a simpler way to control zoom, which can
-     * be a combination of optical and digital zoom. More specifically, for a logical
-     * multi-camera with more than one focal length, using a floating point zoom ratio offers
-     * more zoom precision when a telephoto lens is used, as well as allowing zoom ratio of
-     * less than 1.0 to zoom out to a wide field of view.</p>
-     * <p>Note that the coordinate system of cropRegion, AE/AWB/AF regions, and faces now changes
-     * to the effective after-zoom field-of-view represented by rectangle of (0, 0,
-     * activeArrayWidth, activeArrayHeight).</p>
-     * <p>For example, if {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is 4032*3024, and the preview stream
-     * is configured to the same 4:3 aspect ratio, the application can achieve 2.0x zoom in
-     * one of two ways:</p>
+     * be a combination of optical and digital zoom. For example, a multi-camera system may
+     * contain more than one lens with different focal lengths, and the user can use optical
+     * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
+     * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+     *   better precision compared to an integer value of {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.
+     * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+     *   {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} doesn't.</p>
+     * <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
+     * and output streams, for a hypothetical camera device with an active array of size
+     * <code>(2000,1500)</code>.</p>
      * <ul>
-     * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 4032, 3024)</li>
-     * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (1008, 756, 3024, 2268)</li>
+     * <li>Camera Configuration:<ul>
+     * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+     * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+     * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
      * </ul>
-     * <p>If the application intends to set aeRegions to be top-left quarter of the preview
-     * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 2016, 1512) with
+     * </li>
+     * <li>Case #1: 4:3 crop region with 2.0x zoom ratio<ul>
+     * <li>Zoomed field of view: 1/4 of original field of view</li>
+     * <li>Crop region: <code>Rect(0, 0, 2000, 1500) // (left, top, right, bottom)</code> (post zoom)</li>
+     * </ul>
+     * </li>
+     * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png" /><ul>
+     * <li><code>640x480</code> stream source area: <code>(0, 0, 2000, 1500)</code> (equal to crop region)</li>
+     * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #2: 16:9 crop region with 2.0x zoom.<ul>
+     * <li>Zoomed field of view: 1/4 of original field of view</li>
+     * <li>Crop region: <code>Rect(0, 187, 2000, 1312)</code></li>
+     * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (equal to crop region)</li>
+     * </ul>
+     * </li>
+     * <li>Case #3: 1:1 crop region with 0.5x zoom out to ultrawide lens.<ul>
+     * <li>Zoomed field of view: 4x of original field of view (switched from wide lens to ultrawide lens)</li>
+     * <li>Crop region: <code>Rect(250, 0, 1750, 1500)</code></li>
+     * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (letterboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(250, 328, 1750, 1172)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * </ul>
+     * <p>As seen from the graphs above, the coordinate system of cropRegion now changes to the
+     * effective after-zoom field-of-view, and is represented by the rectangle of (0, 0,
+     * activeArrayWith, activeArrayHeight). The same applies to AE/AWB/AF regions, and faces.
+     * This coordinate system change isn't applicable to RAW capture and its related
+     * metadata such as intrinsicCalibration and lensShadingMap.</p>
+     * <p>Using the same hypothetical example above, and assuming output stream #1 (640x480) is
+     * the viewfinder stream, the application can achieve 2.0x zoom in one of two ways:</p>
+     * <ul>
+     * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 2000, 1500)</li>
+     * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (500, 375, 1500, 1125)</li>
+     * </ul>
+     * <p>If the application intends to set aeRegions to be top-left quarter of the viewfinder
+     * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 1000, 750) with
      * zoomRatio set to 2.0. Alternatively, the application can set aeRegions to the equivalent
-     * region of (1008, 756, 2016, 1512) for zoomRatio of 1.0. If the application doesn't
+     * region of (500, 375, 1000, 750) for zoomRatio of 1.0. If the application doesn't
      * explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
-     * <p>This coordinate system change isn't applicable to RAW capture and its related metadata
-     * such as intrinsicCalibration and lensShadingMap.</p>
      * <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
      * must only be used for letterboxing or pillarboxing of the sensor active array, and no
      * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
@@ -2446,7 +2485,6 @@
      * @see CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CaptureRequest#SCALER_CROP_REGION
-     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     @NonNull
@@ -2848,12 +2886,15 @@
      * frames before the lens can change to the requested focal length.
      * While the focal length is still changing, {@link CaptureResult#LENS_STATE android.lens.state} will
      * be set to MOVING.</p>
-     * <p>Optical zoom will not be supported on most devices.</p>
+     * <p>Optical zoom via this control will not be supported on most devices. Starting from API
+     * level 30, the camera device may combine optical and digital zoom through the
+     * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} control.</p>
      * <p><b>Units</b>: Millimeters</p>
      * <p><b>Range of valid values:</b><br>
      * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS android.lens.info.availableFocalLengths}</p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_ZOOM_RATIO
      * @see CaptureRequest#LENS_APERTURE
      * @see CaptureRequest#LENS_FOCUS_DISTANCE
      * @see CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e..9b3e8c9 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@
             }
             return;
         }
-
+        // The IME doesn't have information about the virtual view id for the child views in the
+        // web view, so we are only comparing the parent view id here. This means that for cases
+        // where there are two input fields in the web view, they will have the same view id
+        // (although different virtual child id), and we will not be able to distinguish them.
+        final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get();
         if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())
-                || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) {
+                || imeClientFieldId == null
+                || fieldId.getViewId() != imeClientFieldId.getViewId()) {
             if (DEBUG) {
                 Log.d(TAG,
                         "handleOnInlineSuggestionsResponse() called on the wrong package/field "
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11..1873d77 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@
  * @hide
  */
 @SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
     private final int mError;
 
     // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2c356e4..7ff954b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -690,9 +690,9 @@
             route.getMtu());
     }
 
-    private int findRouteIndexByDestination(RouteInfo route) {
+    private int findRouteIndexByRouteKey(RouteInfo route) {
         for (int i = 0; i < mRoutes.size(); i++) {
-            if (mRoutes.get(i).isSameDestinationAs(route)) {
+            if (mRoutes.get(i).getRouteKey().equals(route.getRouteKey())) {
                 return i;
             }
         }
@@ -701,11 +701,11 @@
 
     /**
      * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
-     * with the same destination exists with different properties (e.g., different MTU),
-     * it will be updated. If the {@link RouteInfo} had an interface name set and
-     * that differs from the interface set for this {@code LinkProperties} an
-     * {@link IllegalArgumentException} will be thrown.  The proper
-     * course is to add either un-named or properly named {@link RouteInfo}.
+     * with the same {@link RouteInfo.RouteKey} with different properties
+     * (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an
+     * interface name set and that differs from the interface set for this
+     * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.
+     * The proper course is to add either un-named or properly named {@link RouteInfo}.
      *
      * @param route A {@link RouteInfo} to add to this object.
      * @return {@code true} was added or updated, false otherwise.
@@ -719,7 +719,7 @@
         }
         route = routeWithInterface(route);
 
-        int i = findRouteIndexByDestination(route);
+        int i = findRouteIndexByRouteKey(route);
         if (i == -1) {
             // Route was not present. Add it.
             mRoutes.add(route);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..05d7860 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -2000,7 +2000,7 @@
      */
     @SystemApi
     @TestApi
-    public static class Builder {
+    public static final class Builder {
         private final NetworkCapabilities mCaps;
 
         /**
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index fec2df4..dbdaa4c 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -26,6 +26,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -527,23 +528,27 @@
     }
 
     /**
-     * Compares this RouteInfo object against the specified object and indicates if the
-     * destinations of both routes are equal.
-     * @return {@code true} if the route destinations are equal, {@code false} otherwise.
+     * A helper class that contains the destination and the gateway in a {@code RouteInfo},
+     * used by {@link ConnectivityService#updateRoutes} or
+     * {@link LinkProperties#addRoute} to calculate the list to be updated.
      *
      * @hide
      */
-    public boolean isSameDestinationAs(@Nullable Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof RouteInfo)) return false;
-
-        RouteInfo target = (RouteInfo) obj;
-
-        if (Objects.equals(mDestination, target.getDestination())) {
-            return true;
+    public static class RouteKey extends Pair<IpPrefix, InetAddress> {
+        RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway) {
+            super(destination, gateway);
         }
-        return false;
+    }
+
+    /**
+     * Get {@code RouteKey} of this {@code RouteInfo}.
+     * @return a {@code RouteKey} object.
+     *
+     * @hide
+     */
+    @NonNull
+    public RouteKey getRouteKey() {
+        return new RouteKey(mDestination, mGateway);
     }
 
     /**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 785b51d..cdc0019 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2224,15 +2224,27 @@
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
      * This broadcast is only sent to registered receivers.
      *
+     * @deprecated This is sent at the same time as {@link #ACTION_POWER_SAVE_MODE_CHANGED} so it
+     * does not provide advanced warning. As such it will be removed in future Android versions.
+     * Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} and {@link #isPowerSaveMode()} instead.
+     *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} instead.")
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_POWER_SAVE_MODE_CHANGING
             = "android.os.action.POWER_SAVE_MODE_CHANGING";
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * @deprecated Use {@link #isPowerSaveMode()} instead.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #isPowerSaveMode()} instead.")
+    @Deprecated
     public static final String EXTRA_POWER_SAVE_MODE = "mode";
 
     /**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 71f931d..5cc73ca 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -72,16 +72,16 @@
      * V4 signature data.
      */
     public static class SigningInfo {
-        public final byte[] v3Digest;  // used to match with the corresponding APK
+        public final byte[] apkDigest;  // used to match with the corresponding APK
         public final byte[] certificate; // ASN.1 DER form
         public final byte[] additionalData; // a free-form binary data blob
         public final byte[] publicKey; // ASN.1 DER, must match the certificate
         public final int signatureAlgorithmId; // see the APK v2 doc for the list
         public final byte[] signature;
 
-        SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+        SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
                 byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
-            this.v3Digest = v3Digest;
+            this.apkDigest = apkDigest;
             this.certificate = certificate;
             this.additionalData = additionalData;
             this.publicKey = publicKey;
@@ -94,13 +94,13 @@
          */
         public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
             ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
-            byte[] v3Digest = readBytes(buffer);
+            byte[] apkDigest = readBytes(buffer);
             byte[] certificate = readBytes(buffer);
             byte[] additionalData = readBytes(buffer);
             byte[] publicKey = readBytes(buffer);
             int signatureAlgorithmId = buffer.getInt();
             byte[] signature = readBytes(buffer);
-            return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+            return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
                     signatureAlgorithmId, signature);
         }
     }
@@ -150,7 +150,7 @@
         final int size =
                 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
                         hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
-                        signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+                        signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
                         signingInfo.additionalData);
         ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
         buffer.putInt(size);
@@ -159,7 +159,7 @@
         buffer.put(hashingInfo.log2BlockSize);
         writeBytes(buffer, hashingInfo.salt);
         writeBytes(buffer, hashingInfo.rawRootHash);
-        writeBytes(buffer, signingInfo.v3Digest);
+        writeBytes(buffer, signingInfo.apkDigest);
         writeBytes(buffer, signingInfo.certificate);
         writeBytes(buffer, signingInfo.additionalData);
         return buffer.array();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ccc3132..60f10cd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10339,6 +10339,7 @@
 
         /**
          * Value to specify if wifi settings migration is complete or not.
+         * Note: This should only be used from within {@link android.net.wifi.WifiMigration} class.
          *
          * Type: int (0 for false, 1 for true)
          * @hide
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d070b1..f8265d6 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -780,15 +780,16 @@
             audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION;
         }
 
-        int code = STATUS_ERROR;
+        int code;
         try {
             code = mModelManagementService.startRecognition(
                     mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
                     new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
                             recognitionExtra, null /* additional data */, audioCapabilities));
         } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in startRecognition!", e);
+            throw e.rethrowFromSystemServer();
         }
+
         if (code != STATUS_OK) {
             Slog.w(TAG, "startRecognition() failed with error code " + code);
         }
@@ -796,12 +797,12 @@
     }
 
     private int stopRecognitionLocked() {
-        int code = STATUS_ERROR;
+        int code;
         try {
             code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id,
                     mInternalCallback);
         } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in stopRecognition!", e);
+            throw e.rethrowFromSystemServer();
         }
 
         if (code != STATUS_OK) {
@@ -968,12 +969,12 @@
                 }
             }
 
-            ModuleProperties dspModuleProperties = null;
+            ModuleProperties dspModuleProperties;
             try {
                 dspModuleProperties =
                         mModelManagementService.getDspModuleProperties();
             } catch (RemoteException e) {
-                Slog.w(TAG, "RemoteException in getDspProperties!", e);
+                throw e.rethrowFromSystemServer();
             }
 
             // No DSP available
@@ -989,7 +990,7 @@
                 mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
                         mText, mLocale.toLanguageTag());
             } catch (RemoteException e) {
-                Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
+                throw e.rethrowFromSystemServer();
             }
         }
     }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b54e4d9..45d3465 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -35,6 +35,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceActionCheckCallback;
@@ -47,6 +48,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -63,6 +65,8 @@
  * separate process from this one.
  */
 public class VoiceInteractionService extends Service {
+    static final String TAG = VoiceInteractionService.class.getSimpleName();
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
@@ -240,9 +244,22 @@
     public void onReady() {
         mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+        Objects.requireNonNull(mSystemService);
+        try {
+            mSystemService.asBinder().linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "unable to link to death with system service");
+        }
         mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
     }
 
+    private IBinder.DeathRecipient mDeathRecipient = () -> {
+        Log.e(TAG, "system service binder died shutting down");
+        Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+    };
+
+
     private void onShutdownInternal() {
         onShutdown();
         // Stop any active recognitions when shutting down.
@@ -349,16 +366,24 @@
     }
 
     private void safelyShutdownHotwordDetector() {
-        try {
-            synchronized (mLock) {
-                if (mHotwordDetector != null) {
-                    mHotwordDetector.stopRecognition();
-                    mHotwordDetector.invalidate();
-                    mHotwordDetector = null;
-                }
+        synchronized (mLock) {
+            if (mHotwordDetector == null) {
+                return;
             }
-        } catch (Exception ex) {
-            // Ignore.
+
+            try {
+                mHotwordDetector.stopRecognition();
+            } catch (Exception ex) {
+                // Ignore.
+            }
+
+            try {
+                mHotwordDetector.invalidate();
+            } catch (Exception ex) {
+                // Ignore.
+            }
+
+            mHotwordDetector = null;
         }
     }
 
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 04be71f..346fe29 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,6 +24,7 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.util.ArrayMap;
@@ -117,7 +118,10 @@
         return vSigner.certs;
     }
 
-    private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
+    /**
+     * Same as above returns the full signer object, containing additional info e.g. digest.
+     */
+    public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
             throws SignatureNotFoundException, SecurityException, IOException {
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
             return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
+        byte[] digest = pickBestDigestForV4(contentDigests);
+
         return new VerifiedSigner(
                 signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
-                verityRootHash);
+                verityRootHash, digest);
     }
 
     private static X509Certificate[] verifySigner(
@@ -426,11 +432,14 @@
      */
     public static class VerifiedSigner {
         public final X509Certificate[][] certs;
-        public final byte[] verityRootHash;
 
-        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+        public final byte[] verityRootHash;
+        public final byte[] digest;
+
+        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
             this.certs = certs;
             this.verityRootHash = verityRootHash;
+            this.digest = digest;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 2437af2..4ab541b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,8 +16,6 @@
 
 package android.util.apk;
 
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -26,6 +24,7 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.os.Build;
@@ -213,24 +212,11 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
-        result.digest = pickBestV3DigestForV4(contentDigests);
+        result.digest = pickBestDigestForV4(contentDigests);
 
         return result;
     }
 
-    // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
-    private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
-        final int[] orderedContentDigestTypes =
-                {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
-                        CONTENT_DIGEST_CHUNKED_SHA256};
-        for (int contentDigestType : orderedContentDigestTypes) {
-            if (contentDigests.containsKey(contentDigestType)) {
-                return contentDigests.get(contentDigestType);
-            }
-        }
-        return null;
-    }
-
     private static VerifiedSigner verifySigner(
             ByteBuffer signerBlock,
             Map<Integer, byte[]> contentDigests,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 8c240d9..d40efce 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -145,7 +145,7 @@
                     "Public key mismatch between certificate and signature record");
         }
 
-        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
     }
 
     /**
@@ -155,11 +155,11 @@
      */
     public static class VerifiedSigner {
         public final Certificate[] certs;
-        public byte[] v3Digest;
+        public byte[] apkDigest;
 
-        public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+        public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
             this.certs = certs;
-            this.v3Digest = v3Digest;
+            this.apkDigest = apkDigest;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index c1cee48..ab8f80d3 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -184,27 +184,45 @@
             Signature[] signerSigs = convertToSignatures(signerCerts);
 
             if (verifyFull) {
-                // v4 is an add-on and requires v3 signature to validate against its certificates
-                ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
-                        ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
-                Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
-                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+                byte[] nonstreamingDigest = null;
+                Certificate[][] nonstreamingCerts = null;
 
+                try {
+                    // v4 is an add-on and requires v2 or v3 signature to validate against its
+                    // certificate and digest
+                    ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
+                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+                    nonstreamingDigest = v3Signer.digest;
+                    nonstreamingCerts = new Certificate[][]{v3Signer.certs};
+                } catch (SignatureNotFoundException e) {
+                    try {
+                        ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
+                                ApkSignatureSchemeV2Verifier.verify(apkPath, false);
+                        nonstreamingDigest = v2Signer.digest;
+                        nonstreamingCerts = v2Signer.certs;
+                    } catch (SignatureNotFoundException ee) {
+                        throw new SecurityException(
+                                "V4 verification failed to collect V2/V3 certificates from : "
+                                        + apkPath, ee);
+                    }
+                }
+
+                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
                 if (nonstreamingSigs.length != signerSigs.length) {
                     throw new SecurityException(
-                            "Invalid number of certificates: " + nonstreaming.certs.length);
+                            "Invalid number of certificates: " + nonstreamingSigs.length);
                 }
 
                 for (int i = 0, size = signerSigs.length; i < size; ++i) {
                     if (!nonstreamingSigs[i].equals(signerSigs[i])) {
-                        throw new SecurityException("V4 signature certificate does not match V3");
+                        throw new SecurityException(
+                                "V4 signature certificate does not match V2/V3");
                     }
                 }
 
-                // TODO(b/151240006): add support for v2 digest and make it mandatory.
-                if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
-                        nonstreaming.digest, vSigner.v3Digest.length)) {
-                    throw new SecurityException("V3 digest in V4 signature does not match V3");
+                if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+                        vSigner.apkDigest.length)) {
+                    throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                 }
             }
 
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 4fe8515..2a4b65d 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -421,6 +421,10 @@
     static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
     static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
 
+    private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
+            {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                    CONTENT_DIGEST_CHUNKED_SHA256};
+
     static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
         int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
         int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
@@ -572,6 +576,21 @@
     }
 
     /**
+     * Returns the best digest from the map of available digests.
+     * similarly to compareContentDigestAlgorithm.
+     *
+     * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
+     */
+    static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
+        for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
+            if (contentDigests.containsKey(algo)) {
+                return contentDigests.get(algo);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns new byte buffer whose content is a shared subsequence of this buffer's content
      * between the specified start (inclusive) and end (exclusive) positions. As opposed to
      * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 95b2c70..30e3ec1 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -35,7 +35,7 @@
     }
 
     @Override
-    public void onBatchedInputEventPending() {
+    public void onBatchedInputEventPending(int source) {
         scheduleBatchedInput();
     }
 
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index dfe89a3..45e51f7 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -318,8 +318,7 @@
      * Called when the client has changed the local insets state, and now the server should reflect
      * that new state.
      */
-    void insetsModified(IWindow window, in InsetsState state);
-
+    oneway void insetsModified(IWindow window, in InsetsState state);
 
     /**
      * Called when the system gesture exclusion has changed.
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 7986ceb..25a4108 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -147,8 +147,9 @@
      * samples until the recipient calls {@link #consumeBatchedInputEvents} or
      * an event is received that ends the batch and causes it to be consumed
      * immediately (such as a pointer up event).
+     * @param source The source of the batched event.
      */
-    public void onBatchedInputEventPending() {
+    public void onBatchedInputEventPending(int source) {
         consumeBatchedInputEvents(-1);
     }
 
@@ -219,13 +220,6 @@
         onInputEvent(event);
     }
 
-    // Called from native code.
-    @SuppressWarnings("unused")
-    @UnsupportedAppUsage
-    private void dispatchBatchedInputEventPending() {
-        onBatchedInputEventPending();
-    }
-
     /**
      * Factory for InputEventReceiver
      */
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index b700728..07b6cc1 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -258,7 +258,6 @@
         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
                 false /* isScreenRound */,
                 false /* alwaysConsumeSystemBars */, null /* displayCutout */,
-                null /* legacyContentInsets */, null /* legacyStableInsets */,
                 LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
                 0 /* legacySystemUiFlags */, typeSideMap)
                .getInsets(mTypes);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 048c0e2..e6bd843 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toInternalType;
 import static android.view.InsetsState.toPublicType;
 import static android.view.WindowInsets.Type.all;
 import static android.view.WindowInsets.Type.ime;
@@ -26,8 +27,6 @@
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -38,11 +37,9 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.renderscript.Sampler.Value;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
-import android.util.Property;
 import android.util.SparseArray;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
@@ -363,9 +360,6 @@
 
     private final Runnable mAnimCallback;
 
-    private final Rect mLastLegacyContentInsets = new Rect();
-    private final Rect mLastLegacyStableInsets = new Rect();
-
     /** Pending control request that is waiting on IME to be ready to be shown */
     private PendingControlRequest mPendingImeControlRequest;
 
@@ -435,8 +429,8 @@
 
             WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
                     mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
-                    mLastDisplayCutout, mLastLegacyContentInsets, mLastLegacyStableInsets,
-                    mLastLegacySoftInputMode, mLastLegacySystemUiFlags, null /* typeSideMap */);
+                    mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
+                    null /* typeSideMap */);
             mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets,
                     mUnmodifiableTmpRunningAnims);
 
@@ -466,44 +460,58 @@
 
     @VisibleForTesting
     public boolean onStateChanged(InsetsState state) {
-        if (mState.equals(state) && mLastDispachedState.equals(state)) {
+        boolean localStateChanged = !mState.equals(state);
+        if (!localStateChanged && mLastDispachedState.equals(state)) {
             return false;
         }
-        mState.set(state);
+        updateState(state);
         mLastDispachedState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
-        mViewRoot.notifyInsetsChanged();
+        if (localStateChanged) {
+            mViewRoot.notifyInsetsChanged();
+        }
         if (!mState.equals(mLastDispachedState)) {
             sendStateToWindowManager();
         }
         return true;
     }
 
+    private void updateState(InsetsState newState) {
+        mState.setDisplayFrame(newState.getDisplayFrame());
+        for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = newState.sourceAt(i);
+            getSourceConsumer(source.getType()).updateSource(source);
+        }
+        for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = mState.sourceAt(i);
+            if (newState.peekSource(source.getType()) == null) {
+                mState.removeSource(source.getType());
+            }
+        }
+    }
+
     /**
      * @see InsetsState#calculateInsets
      */
     @VisibleForTesting
     public WindowInsets calculateInsets(boolean isScreenRound,
-            boolean alwaysConsumeSystemBars, DisplayCutout cutout, Rect legacyContentInsets,
-            Rect legacyStableInsets, int legacySoftInputMode, int legacySystemUiFlags) {
-        mLastLegacyContentInsets.set(legacyContentInsets);
-        mLastLegacyStableInsets.set(legacyStableInsets);
+            boolean alwaysConsumeSystemBars, DisplayCutout cutout,
+            int legacySoftInputMode, int legacySystemUiFlags) {
         mLastLegacySoftInputMode = legacySoftInputMode;
         mLastLegacySystemUiFlags = legacySystemUiFlags;
         mLastDisplayCutout = cutout;
         mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
-                isScreenRound, alwaysConsumeSystemBars, cutout, legacyContentInsets,
-                legacyStableInsets, legacySoftInputMode, legacySystemUiFlags,
+                isScreenRound, alwaysConsumeSystemBars, cutout,
+                legacySoftInputMode, legacySystemUiFlags,
                 null /* typeSideMap */);
         return mLastInsets;
     }
 
     /**
-     * @see InsetsState#calculateVisibleInsets(Rect, Rect, int)
+     * @see InsetsState#calculateVisibleInsets(Rect, int)
      */
-    public Rect calculateVisibleInsets(Rect legacyVisibleInsets,
-            @SoftInputModeFlags int softInputMode) {
-        return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode);
+    public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
+        return mState.calculateVisibleInsets(mFrame, softInputMode);
     }
 
     /**
@@ -861,8 +869,15 @@
             control.cancel();
         }
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            if (mRunningAnimations.get(i).runner == control) {
+            RunningAnimation runningAnimation = mRunningAnimations.get(i);
+            if (runningAnimation.runner == control) {
                 mRunningAnimations.remove(i);
+                ArraySet<Integer> types = toInternalType(control.getTypes());
+                for (int j = types.size() - 1; j >= 0; j--) {
+                    if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
+                        mViewRoot.notifyInsetsChanged();
+                    }
+                }
                 break;
             }
         }
@@ -954,7 +969,6 @@
             }
         }
 
-        // TODO: Put this on a dispatcher thread.
         try {
             mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
         } catch (RemoteException e) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 3325734..f36621c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,11 +16,13 @@
 
 package android.view;
 
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsState.toPublicType;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
@@ -64,6 +66,8 @@
     private final Supplier<Transaction> mTransactionSupplier;
     private @Nullable InsetsSourceControl mSourceControl;
     private boolean mHasWindowFocus;
+    private Rect mPendingFrame;
+    private Rect mPendingVisibleFrame;
 
     public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
             Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -215,6 +219,38 @@
         // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
     }
 
+    void updateSource(InsetsSource newSource) {
+        InsetsSource source = mState.peekSource(mType);
+        if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+                || source.getFrame().equals(newSource.getFrame())) {
+            mState.addSource(newSource);
+            return;
+        }
+
+        // Frame is changing while animating. Keep note of the new frame but keep existing frame
+        // until animaition is finished.
+        newSource = new InsetsSource(newSource);
+        mPendingFrame = new Rect(newSource.getFrame());
+        mPendingVisibleFrame = newSource.getVisibleFrame() != null
+                ? new Rect(newSource.getVisibleFrame())
+                : null;
+        newSource.setFrame(source.getFrame());
+        newSource.setVisibleFrame(source.getVisibleFrame());
+        mState.addSource(newSource);
+    }
+
+    boolean notifyAnimationFinished() {
+        if (mPendingFrame != null) {
+            InsetsSource source = mState.getSource(mType);
+            source.setFrame(mPendingFrame);
+            source.setVisibleFrame(mPendingVisibleFrame);
+            mPendingFrame = null;
+            mPendingVisibleFrame = null;
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Sets requested visibility from the client, regardless of whether we are able to control it at
      * the moment.
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c2ad74a..40e6f57 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -160,7 +160,6 @@
      */
     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
             boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
-            @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets,
             int legacySoftInputMode, int legacySystemUiFlags,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
         Insets[] typeInsetsMap = new Insets[Type.SIZE];
@@ -168,11 +167,6 @@
         boolean[] typeVisibilityMap = new boolean[SIZE];
         final Rect relativeFrame = new Rect(frame);
         final Rect relativeFrameMax = new Rect(frame);
-        if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
-                && legacyContentInsets != null && legacyStableInsets != null) {
-            WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
-            WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
-        }
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
             InsetsSource source = mSources.get(type);
             if (source == null) {
@@ -217,12 +211,7 @@
                         && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
     }
 
-    public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets,
-            @SoftInputModeFlags int softInputMode) {
-        if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
-            return legacyVisibleInsets;
-        }
-
+    public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
         Insets insets = Insets.NONE;
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
             InsetsSource source = mSources.get(type);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fcab9d1..d69357b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -80,6 +80,7 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -28745,7 +28746,8 @@
          * of the screen decorations, these are the current insets for the
          * content of the window.
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+                publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
         final Rect mContentInsets = new Rect();
 
         /**
@@ -28753,7 +28755,8 @@
          * of the screen decorations, these are the current insets for the
          * actual visible parts of the window.
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+                publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
         final Rect mVisibleInsets = new Rect();
 
         /**
@@ -28761,7 +28764,8 @@
          * of the screen decorations, these are the current insets for the
          * stable system windows.
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+                publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
         final Rect mStableInsets = new Rect();
 
         final DisplayCutout.ParcelableWrapper mDisplayCutout =
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 218c9fb..50202ae 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -504,7 +504,6 @@
     int mPendingInputEventCount;
     boolean mProcessInputEventsScheduled;
     boolean mUnbufferedInputDispatch;
-    boolean mUnbufferedInputDispatchBySource;
     @InputSourceClass
     int mUnbufferedInputSource = SOURCE_CLASS_NONE;
 
@@ -548,13 +547,11 @@
     boolean mAddedTouchMode;
 
     final Rect mTmpFrame = new Rect();
+    final Rect mTmpRect = new Rect();
 
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
 
-    final Rect mPendingVisibleInsets = new Rect();
-    final Rect mPendingStableInsets = new Rect();
-    final Rect mPendingContentInsets = new Rect();
     final Rect mPendingBackDropFrame = new Rect();
     final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
             new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
@@ -563,10 +560,6 @@
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
-    final Rect mDispatchContentInsets = new Rect();
-    final Rect mDispatchStableInsets = new Rect();
-    DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
-
     private WindowInsets mLastWindowInsets;
 
     // Insets types hidden by legacy window flags or system UI flags.
@@ -1020,10 +1013,7 @@
                 if (mTranslator != null) {
                     mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
                 }
-                mPendingContentInsets.set(mAttachInfo.mContentInsets);
-                mPendingStableInsets.set(mAttachInfo.mStableInsets);
                 mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
-                mPendingVisibleInsets.set(0, 0, 0, 0);
                 mAttachInfo.mAlwaysConsumeSystemBars =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
                 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
@@ -1872,9 +1862,6 @@
             mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
-            if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
-                scheduleConsumeBatchedInput();
-            }
             notifyRendererOfFramePending();
             pokeDrawLockIfNeeded();
         }
@@ -2204,47 +2191,22 @@
 
     /* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
         if (mLastWindowInsets == null || forceConstruct) {
-            mDispatchContentInsets.set(mAttachInfo.mContentInsets);
-            mDispatchStableInsets.set(mAttachInfo.mStableInsets);
-            mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
-
-            Rect contentInsets = mDispatchContentInsets;
-            Rect stableInsets = mDispatchStableInsets;
-            DisplayCutout displayCutout = mDispatchDisplayCutout;
-            // For dispatch we preserve old logic, but for direct requests from Views we allow to
-            // immediately use pending insets. This is such that getRootWindowInsets returns the
-            // result from the layout hint before we ran a traversal shortly after adding a window.
-            if (!forceConstruct
-                    && (!mPendingContentInsets.equals(contentInsets) ||
-                        !mPendingStableInsets.equals(stableInsets) ||
-                        !mPendingDisplayCutout.get().equals(displayCutout))) {
-                contentInsets = mPendingContentInsets;
-                stableInsets = mPendingStableInsets;
-                displayCutout = mPendingDisplayCutout.get();
-            }
-            contentInsets = ensureInsetsNonNegative(contentInsets, "content");
-            stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
             mLastWindowInsets = mInsetsController.calculateInsets(
                     mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeSystemBars, displayCutout,
-                    contentInsets, stableInsets, mWindowAttributes.softInputMode,
-                    (mWindowAttributes.systemUiVisibility
+                    mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(),
+                    mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility
                             | mWindowAttributes.subtreeSystemUiVisibility));
+
+            Rect visibleInsets = mInsetsController.calculateVisibleInsets(
+                    mWindowAttributes.softInputMode);
+
+            mAttachInfo.mVisibleInsets.set(visibleInsets);
+            mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
+            mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
         }
         return mLastWindowInsets;
     }
 
-    private Rect ensureInsetsNonNegative(Rect insets, String kind) {
-        if (insets.left < 0  || insets.top < 0  || insets.right < 0  || insets.bottom < 0) {
-            Log.wtf(mTag, "Negative " + kind + "Insets: " + insets + ", mFirst=" + mFirst);
-            return new Rect(Math.max(0, insets.left),
-                    Math.max(0, insets.top),
-                    Math.max(0, insets.right),
-                    Math.max(0, insets.bottom));
-        }
-        return insets;
-    }
-
     public void dispatchApplyInsets(View host) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets");
         mApplyInsetsRequested = false;
@@ -2266,12 +2228,6 @@
                         == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
     }
 
-    private void updateVisibleInsets() {
-        Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets,
-                mWindowAttributes.softInputMode);
-        mAttachInfo.mVisibleInsets.set(visibleInsets);
-    }
-
     @VisibleForTesting
     public InsetsController getInsetsController() {
         return mInsetsController;
@@ -2407,7 +2363,7 @@
         // Execute enqueued actions on every traversal in case a detached view enqueued an action
         getRunQueue().executeActions(mAttachInfo.mHandler);
 
-        boolean insetsChanged = false;
+        boolean cutoutChanged = false;
 
         boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
         if (layoutRequested) {
@@ -2420,22 +2376,8 @@
                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
                 ensureTouchModeLocally(mAddedTouchMode);
             } else {
-                if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
-                    insetsChanged = true;
-                }
-                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
-                    insetsChanged = true;
-                }
                 if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
-                    insetsChanged = true;
-                }
-                if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
-                    updateVisibleInsets();
-                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
-                            + mAttachInfo.mVisibleInsets);
-                }
-                if (mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars) {
-                    insetsChanged = true;
+                    cutoutChanged = true;
                 }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
@@ -2498,7 +2440,6 @@
         }
 
         if (mApplyInsetsRequested) {
-            updateVisibleInsets();
             dispatchApplyInsets(host);
             if (mLayoutRequested) {
                 // Short-circuit catching a new layout request here, so
@@ -2566,8 +2507,8 @@
             controlInsetsForCompatibility(params);
         }
 
-        if (mFirst || windowShouldResize || insetsChanged ||
-                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
+        if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null
+                || mForceNextWindowRelayout) {
             mForceNextWindowRelayout = false;
 
             if (isViewVisible) {
@@ -2589,7 +2530,7 @@
             }
 
             boolean hwInitialized = false;
-            boolean contentInsetsChanged = false;
+            boolean dispatchApplyInsets = false;
             boolean hadSurface = mSurface.isValid();
 
             try {
@@ -2612,9 +2553,6 @@
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
 
                 if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
-                        + " content=" + mPendingContentInsets.toShortString()
-                        + " visible=" + mPendingVisibleInsets.toShortString()
-                        + " stable=" + mPendingStableInsets.toShortString()
                         + " cutout=" + mPendingDisplayCutout.get().toString()
                         + " surface=" + mSurface);
 
@@ -2631,14 +2569,7 @@
                     updatedConfiguration = true;
                 }
 
-                contentInsetsChanged = !mPendingContentInsets.equals(
-                        mAttachInfo.mContentInsets);
-                final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
-                        mAttachInfo.mVisibleInsets);
-                final boolean stableInsetsChanged = !mPendingStableInsets.equals(
-                        mAttachInfo.mStableInsets);
-                final boolean cutoutChanged = !mPendingDisplayCutout.equals(
-                        mAttachInfo.mDisplayCutout);
+                cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout);
                 surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
                 final boolean alwaysConsumeSystemBarsChanged =
@@ -2649,42 +2580,25 @@
                 surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId())
                         && mSurface.isValid();
 
-                if (contentInsetsChanged) {
-                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
-                    if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
-                            + mAttachInfo.mContentInsets);
-                }
-                if (stableInsetsChanged) {
-                    mAttachInfo.mStableInsets.set(mPendingStableInsets);
-                    if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
-                            + mAttachInfo.mStableInsets);
-                    // Need to relayout with content insets.
-                    contentInsetsChanged = true;
-                }
                 if (cutoutChanged) {
                     mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
                     if (DEBUG_LAYOUT) {
                         Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
                     }
                     // Need to relayout with content insets.
-                    contentInsetsChanged = true;
+                    dispatchApplyInsets = true;
                 }
                 if (alwaysConsumeSystemBarsChanged) {
                     mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
-                    contentInsetsChanged = true;
+                    dispatchApplyInsets = true;
                 }
-                if (contentInsetsChanged || mLastSystemUiVisibility !=
+                if (dispatchApplyInsets || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                     dispatchApplyInsets(host);
                     // We applied insets so force contentInsetsChanged to ensure the
                     // hierarchy is measured below.
-                    contentInsetsChanged = true;
-                }
-                if (visibleInsetsChanged) {
-                    updateVisibleInsets();
-                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
-                            + mAttachInfo.mVisibleInsets);
+                    dispatchApplyInsets = true;
                 }
                 if (colorModeChanged && mAttachInfo.mThreadedRenderer != null) {
                     mAttachInfo.mThreadedRenderer.setWideGamut(
@@ -2775,7 +2689,8 @@
                                         && mWinFrame.height() == mPendingBackDropFrame.height();
                         // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
-                                mPendingVisibleInsets, mPendingStableInsets, mResizeMode);
+                                mLastWindowInsets.getSystemWindowInsets().toRect(),
+                                mLastWindowInsets.getStableInsets().toRect(), mResizeMode);
                     } else {
                         // We shouldn't come here, but if we come we should end the resize.
                         endDragResizing();
@@ -2866,7 +2781,7 @@
                 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                         (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
-                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
+                        || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
                         updatedConfiguration) {
                     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
@@ -2875,7 +2790,7 @@
                             + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                             + " mHeight=" + mHeight
                             + " measuredHeight=" + host.getMeasuredHeight()
-                            + " coveredInsetsChanged=" + contentInsetsChanged);
+                            + " dispatchApplyInsets=" + dispatchApplyInsets);
 
                      // Ask host how big it wants to be
                     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
@@ -4921,12 +4836,9 @@
                     // Recycled in the fall through...
                     SomeArgs args = (SomeArgs) msg.obj;
                     if (mWinFrame.equals(args.arg1)
-                            && mPendingContentInsets.equals(args.arg2)
-                            && mPendingStableInsets.equals(args.arg6)
                             && mPendingDisplayCutout.get().equals(args.arg9)
-                            && mPendingVisibleInsets.equals(args.arg3)
                             && mPendingBackDropFrame.equals(args.arg8)
-                            && args.arg4 == null
+                            && mLastReportedMergedConfiguration.equals(args.arg4)
                             && args.argi1 == 0
                             && mDisplay.getDisplayId() == args.argi3) {
                         break;
@@ -4954,16 +4866,10 @@
                         }
 
                         final boolean framesChanged = !mWinFrame.equals(args.arg1)
-                                || !mPendingContentInsets.equals(args.arg2)
-                                || !mPendingStableInsets.equals(args.arg6)
-                                || !mPendingDisplayCutout.get().equals(args.arg9)
-                                || !mPendingVisibleInsets.equals(args.arg3);
+                                || !mPendingDisplayCutout.get().equals(args.arg9);
 
                         setFrame((Rect) args.arg1);
-                        mPendingContentInsets.set((Rect) args.arg2);
-                        mPendingStableInsets.set((Rect) args.arg6);
                         mPendingDisplayCutout.set((DisplayCutout) args.arg9);
-                        mPendingVisibleInsets.set((Rect) args.arg3);
                         mPendingBackDropFrame.set((Rect) args.arg8);
                         mForceNextWindowRelayout = args.argi1 != 0;
                         mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
@@ -7417,10 +7323,9 @@
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
-                mTmpFrame, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize,
-                mBlastSurfaceControl);
+                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
+                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
+                mSurfaceSize, mBlastSurfaceControl);
         if (mSurfaceControl.isValid()) {
             if (!mUseBLASTAdapter) {
                 mSurface.copyFrom(mSurfaceControl);
@@ -7441,9 +7346,6 @@
 
         if (mTranslator != null) {
             mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
-            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
-            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
-            mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
         setFrame(mTmpFrame);
         mInsetsController.onStateChanged(mTempInsets);
@@ -8156,7 +8058,6 @@
         @Override
         public void onInputEvent(InputEvent event) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
-            processUnbufferedRequest(event);
             List<InputEvent> processedEvents;
             try {
                 processedEvents =
@@ -8181,12 +8082,18 @@
         }
 
         @Override
-        public void onBatchedInputEventPending() {
-            if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
-                super.onBatchedInputEventPending();
-            } else {
-                scheduleConsumeBatchedInput();
+        public void onBatchedInputEventPending(int source) {
+            final boolean unbuffered = mUnbufferedInputDispatch
+                    || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+            if (unbuffered) {
+                if (mConsumeBatchedInputScheduled) {
+                    unscheduleConsumeBatchedInput();
+                }
+                // Consume event immediately if unbuffered input dispatch has been requested.
+                consumeBatchedInputEvents(-1);
+                return;
             }
+            scheduleConsumeBatchedInput();
         }
 
         @Override
@@ -8199,17 +8106,6 @@
             unscheduleConsumeBatchedInput();
             super.dispose();
         }
-
-        private void processUnbufferedRequest(InputEvent event) {
-            if (!(event instanceof MotionEvent)) {
-                return;
-            }
-            mUnbufferedInputDispatchBySource =
-                    (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
-            if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) {
-                scheduleConsumeBatchedInputImmediately();
-            }
-        }
     }
     WindowInputEventReceiver mInputEventReceiver;
 
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 4050da1..8bf1ade 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -278,7 +278,7 @@
             if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
                 return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
                         isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
-                        systemWindowInsets, stableInsets, SOFT_INPUT_ADJUST_NOTHING,
+                        SOFT_INPUT_ADJUST_NOTHING,
                         SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
             } else {
                 return new WindowInsets.Builder()
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10..62dd192 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@
     // The actual zoom value may changes based on this initial zoom value.
     private float mInitialZoom = 1f;
 
+    // For calculating the line change slops while moving cursor/selection.
+    // The slop max/min value include line height and the slop on the upper/lower line.
+    private static final int LINE_CHANGE_SLOP_MAX_DP = 45;
+    private static final int LINE_CHANGE_SLOP_MIN_DP = 12;
+    private int mLineChangeSlopMax;
+    private int mLineChangeSlopMin;
+
     Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
@@ -430,6 +437,14 @@
             logCursor("Editor", "New magnifier is %s.",
                     mNewMagnifierEnabled ? "enabled" : "disabled");
         }
+
+        mLineChangeSlopMax = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP,
+                mTextView.getContext().getResources().getDisplayMetrics());
+        mLineChangeSlopMin = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP,
+                mTextView.getContext().getResources().getDisplayMetrics());
+
     }
 
     @VisibleForTesting
@@ -6018,7 +6033,14 @@
         }
     }
 
-    private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+    @VisibleForTesting
+    public void setLineChangeSlopMinMaxForTesting(final int min, final int max) {
+        mLineChangeSlopMin = min;
+        mLineChangeSlopMax = max;
+    }
+
+    @VisibleForTesting
+    public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
         final int trueLine = mTextView.getLineAtCoordinate(y);
         if (layout == null || prevLine > layout.getLineCount()
                 || layout.getLineCount() <= 0 || prevLine < 0) {
@@ -6031,28 +6053,21 @@
             return trueLine;
         }
 
+        final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
+        int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS
+                * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+        slop = Math.max(mLineChangeSlopMin,
+                Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
+        slop = Math.max(0, slop);
+
         final float verticalOffset = mTextView.viewportToContentVerticalOffset();
-        final int lineCount = layout.getLineCount();
-        final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
-
-        final float firstLineTop = layout.getLineTop(0) + verticalOffset;
-        final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
-        final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
-
-        final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
-        final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
-        final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
-
-        // Determine if we've moved lines based on y position and previous line.
-        int currLine;
-        if (y <= yTopBound) {
-            currLine = Math.max(prevLine - 1, 0);
-        } else if (y >= yBottomBound) {
-            currLine = Math.min(prevLine + 1, lineCount - 1);
-        } else {
-            currLine = prevLine;
+        if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) {
+            return trueLine;
         }
-        return currLine;
+        if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+            return trueLine;
+        }
+        return prevLine;
     }
 
     /**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 7d97a91..4f14539 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@
     @Nullable
     private CharSequence mText;
 
-    // TODO(b/144152069): Remove this after assessing impact on dogfood.
-    private boolean mIsCustomToast;
-
     /**
      * Construct an empty Toast object.  You must call {@link #setView} before you
      * can call {@link #show}.
@@ -214,8 +211,7 @@
                     service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
                 }
             } else {
-                service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
-                        mIsCustomToast);
+                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
             }
         } catch (RemoteException e) {
             // Empty
@@ -253,7 +249,6 @@
      */
     @Deprecated
     public void setView(View view) {
-        mIsCustomToast = true;
         mNextView = view;
     }
 
@@ -755,8 +750,9 @@
     /**
      * Callback object to be called when the toast is shown or hidden.
      *
-     * Callback methods will be called on the looper thread provided on construction.
+     * <p>Callback methods will be called on the looper thread used for the {@link Toast} object.
      *
+     * @see #makeText(Context, Looper, CharSequence, int)
      * @see #addCallback(Callback)
      */
     public abstract static class Callback {
diff --git a/core/java/android/window/IDisplayAreaOrganizer.aidl b/core/java/android/window/IDisplayAreaOrganizer.aidl
new file mode 100644
index 0000000..1045ab2
--- /dev/null
+++ b/core/java/android/window/IDisplayAreaOrganizer.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.window;
+
+import android.window.IWindowContainer;
+
+/**
+ * Interface for WindowManager to delegate control of display areas.
+ * {@hide}
+ */
+oneway interface IDisplayAreaOrganizer {
+    void onDisplayAreaAppeared(in IWindowContainer displayArea);
+    void onDisplayAreaVanished(in IWindowContainer displayArea);
+}
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
new file mode 100644
index 0000000..fc6fbef
--- /dev/null
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2020, 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.window;
+
+import android.window.IDisplayAreaOrganizer;
+
+/** @hide */
+interface IDisplayAreaOrganizerController {
+
+    /** Register a DisplayAreaOrganizer to manage display areas for a given feature. */
+    void registerOrganizer(in IDisplayAreaOrganizer organizer, int displayAreaFeature);
+}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 0a04462..fcf4830 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -25,8 +25,8 @@
  * {@hide}
  */
 oneway interface ITaskOrganizer {
-    void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
-    void taskVanished(in ActivityManager.RunningTaskInfo taskInfo);
+    void onTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
+    void onTaskVanished(in ActivityManager.RunningTaskInfo taskInfo);
 
     /**
      * Will fire when core attributes of a Task's info change. Relevant properties include the
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 4b47924..7f4b26d 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import android.window.IDisplayAreaOrganizerController;
 import android.window.ITaskOrganizerController;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.WindowContainerTransaction;
@@ -43,4 +44,7 @@
 
     /** @return An interface enabling the management of task organizers. */
     ITaskOrganizerController getTaskOrganizerController();
+
+    /** @return An interface enabling the management of display area organizers. */
+    IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
 }
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index b8969c1..4bd5b29 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -152,6 +152,50 @@
                         }
                     }
                 };
+    }
+
+    /** Class for organizing display areas. */
+    public static class DisplayAreaOrganizer {
+
+        public static final int FEATURE_UNDEFINED = -1;
+        public static final int FEATURE_SYSTEM_FIRST = 0;
+        // The Root display area on a display
+        public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;
+        // Display area hosting the task container.
+        public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
+        // Display area hosting non-activity window tokens.
+        public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;
+
+        public static final int FEATURE_SYSTEM_LAST = 10_000;
+
+        // Vendor specific display area definition can start with this value.
+        public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1;
+
+        /** @hide */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static void registerOrganizer(
+                IDisplayAreaOrganizer organizer, int displayAreaFeature) throws RemoteException {
+            getController().registerOrganizer(organizer, displayAreaFeature);
+        }
+
+        /** @hide */
+        private static IDisplayAreaOrganizerController getController() {
+            return IDisplayAreaOrganizerControllerSingleton.get();
+        }
+
+        private static final Singleton<IDisplayAreaOrganizerController>
+                IDisplayAreaOrganizerControllerSingleton =
+                new Singleton<IDisplayAreaOrganizerController>() {
+                    @Override
+                    protected IDisplayAreaOrganizerController create() {
+                        try {
+                            return getWindowOrganizerController()
+                                    .getDisplayAreaOrganizerController();
+                        } catch (RemoteException e) {
+                            return null;
+                        }
+                    }
+                };
 
     }
 }
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 649e5d2..cc94d6f 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -49,7 +49,7 @@
 
     jmethodID dispatchInputEvent;
     jmethodID onFocusEvent;
-    jmethodID dispatchBatchedInputEventPending;
+    jmethodID onBatchedInputEventPending;
 } gInputEventReceiverClassInfo;
 
 
@@ -242,37 +242,40 @@
 
         status_t status = mInputConsumer.consume(&mInputEventFactory,
                 consumeBatches, frameTime, &seq, &inputEvent);
-        if (status) {
-            if (status == WOULD_BLOCK) {
-                if (!skipCallbacks && !mBatchedInputEventPending
-                        && mInputConsumer.hasPendingBatch()) {
-                    // There is a pending batch.  Come back later.
-                    if (!receiverObj.get()) {
-                        receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
-                        if (!receiverObj.get()) {
-                            ALOGW("channel '%s' ~ Receiver object was finalized "
-                                    "without being disposed.", getInputChannelName().c_str());
-                            return DEAD_OBJECT;
-                        }
-                    }
+        if (status != OK && status != WOULD_BLOCK) {
+            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
+                  getInputChannelName().c_str(), status);
+            return status;
+        }
 
-                    mBatchedInputEventPending = true;
-                    if (kDebugDispatchCycle) {
-                        ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
-                                getInputChannelName().c_str());
-                    }
-                    env->CallVoidMethod(receiverObj.get(),
-                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
-                    if (env->ExceptionCheck()) {
-                        ALOGE("Exception dispatching batched input events.");
-                        mBatchedInputEventPending = false; // try again later
+        if (status == WOULD_BLOCK) {
+            if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) {
+                // There is a pending batch.  Come back later.
+                if (!receiverObj.get()) {
+                    receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
+                    if (!receiverObj.get()) {
+                        ALOGW("channel '%s' ~ Receiver object was finalized "
+                              "without being disposed.",
+                              getInputChannelName().c_str());
+                        return DEAD_OBJECT;
                     }
                 }
-                return OK;
+
+                mBatchedInputEventPending = true;
+                if (kDebugDispatchCycle) {
+                    ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
+                          getInputChannelName().c_str());
+                }
+
+                env->CallVoidMethod(receiverObj.get(),
+                                    gInputEventReceiverClassInfo.onBatchedInputEventPending,
+                                    mInputConsumer.getPendingBatchSource());
+                if (env->ExceptionCheck()) {
+                    ALOGE("Exception dispatching batched input events.");
+                    mBatchedInputEventPending = false; // try again later
+                }
             }
-            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
-                    getInputChannelName().c_str(), status);
-            return status;
+            return OK;
         }
         assert(inputEvent);
 
@@ -441,8 +444,9 @@
             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
     gInputEventReceiverClassInfo.onFocusEvent =
             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
-    gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
-            gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
+    gInputEventReceiverClassInfo.onBatchedInputEventPending =
+            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
+                             "(I)V");
 
     return res;
 }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2..ab57e3d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@
     // Open: Settings > Sound > Do Not Disturb > People > Messages
     // OS: R
     DND_MESSAGES = 1839;
+
+    // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+    // OS: R
+    DND_APPS_BYPASSING = 1840;
 }
diff --git a/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml
new file mode 100644
index 0000000..786f5e6
--- /dev/null
+++ b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?attr/colorAccent">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="?attr/colorControlHighlight" />
+            <corners android:radius="?attr/buttonCornerRadius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index af803fc..210feaf 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -57,11 +57,11 @@
         android:text="@string/resolver_switch_on_work"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:background="@null"
         android:fontFamily="@string/config_headlineFontFamilyMedium"
         android:textSize="14sp"
         android:textColor="?attr/colorAccent"
-        android:layout_centerHorizontal="true" />
+        android:layout_centerHorizontal="true"
+        android:background="@drawable/resolver_turn_on_work_button_ripple_background"/>
     <ProgressBar
         android:id="@+id/resolver_empty_state_progress"
         style="@style/Widget.Material.Light.ProgressBar"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7ec7ecc..06f7760 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -416,10 +416,10 @@
 
     <!-- Content title for a notification. This notification indicates that the device owner has
          changed the location settings. [CHAR LIMIT=NONE] -->
-    <string name="location_changed_notification_title">Location settings changed by your admin</string>
+    <string name="location_changed_notification_title">Apps can access your location</string>
     <!-- Content text for a notification. Tapping opens device location settings.
          [CHAR LIMIT=NONE] -->
-    <string name="location_changed_notification_text">Tap to see your location settings.</string>
+    <string name="location_changed_notification_text">Contact your IT admin to learn more</string>
 
     <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
     <string name="country_detector">Country Detector</string>
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 7f0e0d2..03aba25 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -74,7 +74,7 @@
                     false,
                     new DisplayCutout(
                             Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
-                    rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0);
+                    SOFT_INPUT_ADJUST_RESIZE, 0);
             mImeConsumer = new ImeInsetsSourceConsumer(
                     new InsetsState(), Transaction::new, mController);
         });
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index b449bb0..efdb51d 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -31,6 +31,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -162,7 +163,7 @@
                     false,
                     new DisplayCutout(
                             Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
-                    rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0);
+                    SOFT_INPUT_ADJUST_RESIZE, 0);
             mController.onFrameChanged(new Rect(0, 0, 100, 100));
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -638,6 +639,32 @@
         });
     }
 
+    @Test
+    public void testFrameUpdateDuringAnimation() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+
+            // Pretend IME is calling
+            mController.show(ime(), true /* fromIme */);
+
+            InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
+            copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
+            copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7));
+            mController.onStateChanged(copy);
+            assertNotEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertNotEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+            mController.cancelExistingAnimation();
+            assertEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 1d8e0a3..721dc98 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -73,7 +73,7 @@
             mState.getSource(ITYPE_IME).setVisible(true);
             SparseIntArray typeSideMap = new SparseIntArray();
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
             assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
             assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -92,7 +92,7 @@
             mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
             assertEquals(100, insets.getStableInsetBottom());
             assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
             assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -111,7 +111,7 @@
             mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
             mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, null, null, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, 0, 0, null);
             assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
             assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -127,7 +127,7 @@
             mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null);
             assertEquals(0, insets.getSystemWindowInsetBottom());
             assertEquals(100, insets.getInsets(ime()).bottom);
             assertTrue(insets.isVisible(ime()));
@@ -143,11 +143,11 @@
             mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING,
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
                     SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
             assertEquals(100, insets.getSystemWindowInsetTop());
             insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                    DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING,
+                    DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
                     0 /* legacySystemUiFlags */, null);
             assertEquals(0, insets.getSystemWindowInsetTop());
         }
@@ -161,7 +161,7 @@
         mState.getSource(ITYPE_IME).setVisible(true);
         mState.removeSource(ITYPE_IME);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
@@ -255,7 +255,7 @@
             mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
             mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
             Rect visibleInsets = mState.calculateVisibleInsets(
-                    new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN);
+                    new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
             assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
         }
     }
@@ -273,7 +273,7 @@
             mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
             mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
             Rect visibleInsets = mState.calculateVisibleInsets(
-                    new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING);
+                    new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
             assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
         }
     }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index ba27fac..eae1bbc 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -15,11 +15,12 @@
  */
 package android.view.contentcapture;
 
+import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertThrows;
 
+import android.content.ContentCaptureOptions;
 import android.content.Context;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -37,16 +38,20 @@
     @Mock
     private Context mMockContext;
 
-    private ContentCaptureManager mManager;
-
-    @Before
-    public void before() {
-        mManager = new ContentCaptureManager(mMockContext, /* service= */ null,
-                /* options= */ null);
+    @Test
+    public void testConstructor_invalidParametersThrowsException() {
+        assertThrows(NullPointerException.class,
+                () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */
+                        null));
     }
 
     @Test
-    public void testRemoveData_invalid() {
-        assertThrows(NullPointerException.class, () -> mManager.removeData(null));
+    public void testRemoveData_invalidParametersThrowsException() {
+        final IContentCaptureManager mockService = mock(IContentCaptureManager.class);
+        final ContentCaptureOptions options = new ContentCaptureOptions(null);
+        final ContentCaptureManager manager =
+                new ContentCaptureManager(mMockContext, mockService, options);
+
+        assertThrows(NullPointerException.class, () -> manager.removeData(null));
     }
 }
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c..1b5ce8fd 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.dragOnText;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -37,6 +38,9 @@
 import android.app.Instrumentation;
 import android.graphics.Rect;
 import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -489,6 +493,38 @@
     }
 
     @Test
+    public void testLineChangeSlop() throws Throwable {
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Spannable s = new SpannableString("a\nb\nc");
+        s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+        s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+        mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+        Layout layout = tv.getLayout();
+        Editor editor = tv.getEditorForTesting();
+        final float verticalOffset = tv.getExtendedPaddingTop();
+        editor.setLineChangeSlopMinMaxForTesting(30, 65);
+        // Hit top part of upper line, jump to upper line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+                .isEqualTo(0);
+        // Hit bottom part of upper line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+                .isEqualTo(1);
+        // Hit current line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+                .isEqualTo(1);
+        // Hit top part of lower line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+                .isEqualTo(1);
+        // Hit bottom part of lower line, jump to lower line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+                .isEqualTo(2);
+        // Hit lower line of lower line, jump to target line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+                .isEqualTo(2);
+    }
+
+    @Test
     public void testCursorDrag_snapDistance() throws Throwable {
         String text = "line1: This is the 1st line: A\n"
                 + "line2: This is the 2nd line: B\n"
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 42bf03e..f4a358d 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -90,6 +90,7 @@
 {
 public:
     ProtoOutputStream();
+    ProtoOutputStream(sp<EncodedBuffer> buffer);
     ~ProtoOutputStream();
 
     /**
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 7ffd887..96b54c6 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -16,6 +16,7 @@
 #define LOG_TAG "libprotoutil"
 
 #include <stdlib.h>
+#include <sys/mman.h>
 
 #include <android/util/EncodedBuffer.h>
 #include <android/util/protobuf.h>
@@ -82,14 +83,16 @@
 }
 
 // ===========================================================
-EncodedBuffer::EncodedBuffer() : EncodedBuffer(0)
+EncodedBuffer::EncodedBuffer() : EncodedBuffer(BUFFER_SIZE)
 {
 }
 
 EncodedBuffer::EncodedBuffer(size_t chunkSize)
         :mBuffers()
 {
-    mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+    // Align chunkSize to memory page size
+    chunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+    mChunkSize = (chunkSize / PAGE_SIZE + ((chunkSize % PAGE_SIZE == 0) ? 0 : 1)) * PAGE_SIZE;
     mWp = Pointer(mChunkSize);
     mEp = Pointer(mChunkSize);
 }
@@ -98,7 +101,7 @@
 {
     for (size_t i=0; i<mBuffers.size(); i++) {
         uint8_t* buf = mBuffers[i];
-        free(buf);
+        munmap(buf, mChunkSize);
     }
 }
 
@@ -135,7 +138,10 @@
     if (mWp.index() > mBuffers.size()) return NULL;
     uint8_t* buf = NULL;
     if (mWp.index() == mBuffers.size()) {
-        buf = (uint8_t*)malloc(mChunkSize);
+        // Use mmap instead of malloc to ensure memory alignment i.e. no fragmentation so that
+        // the mem region can be immediately reused by the allocator after calling munmap()
+        buf = (uint8_t*)mmap(NULL, mChunkSize, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
 
         if (buf == NULL) return NULL; // This indicates NO_MEMORY
 
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index ea9b79a..fcf82ee 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -26,8 +26,12 @@
 namespace android {
 namespace util {
 
-ProtoOutputStream::ProtoOutputStream()
-        :mBuffer(new EncodedBuffer()),
+ProtoOutputStream::ProtoOutputStream(): ProtoOutputStream(new EncodedBuffer())
+{
+}
+
+ProtoOutputStream::ProtoOutputStream(sp<EncodedBuffer> buffer)
+        :mBuffer(buffer),
          mCopyBegin(0),
          mCompact(false),
          mDepth(0),
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 383202b..fffdd68 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4579,6 +4579,7 @@
      * {@hide}
      */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
         final IAudioService service = getService();
         try {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c0461bc..1d70a0d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -46,6 +46,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -3048,16 +3049,47 @@
          * @param block The linear block object
          * @param offset The byte offset into the input buffer at which the data starts.
          * @param size The number of bytes of valid input data.
-         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
-         *                   may be null for non-encrypted content.
          * @return this object
          * @throws IllegalStateException if a buffer is already set
          */
         public @NonNull QueueRequest setLinearBlock(
                 @NonNull LinearBlock block,
                 int offset,
+                int size) {
+            if (!isAccessible()) {
+                throw new IllegalStateException("The request is stale");
+            }
+            if (mLinearBlock != null || mHardwareBuffer != null) {
+                throw new IllegalStateException("Cannot set block twice");
+            }
+            mLinearBlock = block;
+            mOffset = offset;
+            mSize = size;
+            mCryptoInfo = null;
+            return this;
+        }
+
+        /**
+         * Set an encrypted linear block to this queue request. Exactly one buffer must be
+         * set for a queue request before calling {@link #queue}. It is possible
+         * to use the same {@link LinearBlock} object for multiple queue
+         * requests. The behavior is undefined if the range of the buffer
+         * overlaps for multiple requests, or the application writes into the
+         * region being processed by the codec.
+         *
+         * @param block The linear block object
+         * @param offset The byte offset into the input buffer at which the data starts.
+         * @param size The number of bytes of valid input data.
+         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setEncryptedLinearBlock(
+                @NonNull LinearBlock block,
+                int offset,
                 int size,
-                @Nullable MediaCodec.CryptoInfo cryptoInfo) {
+                @NonNull MediaCodec.CryptoInfo cryptoInfo) {
+            Objects.requireNonNull(cryptoInfo);
             if (!isAccessible()) {
                 throw new IllegalStateException("The request is stale");
             }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 73f08c68..3f59f89 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1305,4 +1305,44 @@
     <string name="delete_blob_text">Delete shared data</string>
     <!-- Shared data: confirmation dialog text when attempting delete some shared data [CHAR LIMIT=NONE] -->
     <string name="delete_blob_confirmation_text">Are you sure you want to delete this shared data?</string>
+
+    <!-- Summary for add user entry in the choice dialog [CHAR LIMIT=none] -->
+    <string name="user_add_user_item_summary">Users have their own apps and content</string>
+    <!-- Summary for add restricted profile entry in the choice dialog [CHAR LIMIT=none] -->
+    <string name="user_add_profile_item_summary">You can restrict access to apps and content from your account</string>
+    <!-- Button text for adding a regular user [CHAR LIMIT=25] -->
+    <string name="user_add_user_item_title">User</string>
+    <!-- Button text for adding a restricted profile [CHAR LIMIT=25] -->
+    <string name="user_add_profile_item_title">Restricted profile</string>
+    <!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
+    <string name="user_add_user_title">Add new user?</string>
+    <!-- Message for add user confirmation dialog - long version. [CHAR LIMIT=none] -->
+    <string name="user_add_user_message_long">You can share this device with other people by creating additional users. Each user has their own space, which they can customize with apps, wallpaper, and so on. Users can also adjust device settings like Wi\u2011Fi that affect everyone.\n\nWhen you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. Accessibility settings and services may not transfer to the new user.</string>
+    <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
+    <string name="user_add_user_message_short">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
+    <!-- Title of dialog to setup a new user [CHAR LIMIT=30] -->
+    <string name="user_setup_dialog_title">Set up user now?</string>
+    <!-- Message in dialog to setup a new user after creation [CHAR LIMIT=none] -->
+    <string name="user_setup_dialog_message">Make sure the person is available to take the device and set up their space</string>
+    <!-- Message in dialog to setup a new restricted profile after creation [CHAR LIMIT=none] -->
+    <string name="user_setup_profile_dialog_message">Set up profile now?</string>
+    <!-- Button text to setup the new user now [CHAR LIMIT=25] -->
+    <string name="user_setup_button_setup_now">Set up now</string>
+    <!-- Button text to setup the new user later [CHAR LIMIT=25] -->
+    <string name="user_setup_button_setup_later">Not now</string>
+    <!-- Title for add user type dialog [CHAR LIMIT=45] -->
+    <string name="user_add_user_type_title">Add</string>
+    <!-- User details new user name [CHAR LIMIT=30] -->
+    <string name="user_new_user_name">New user</string>
+    <!-- User details new restricted profile name [CHAR LIMIT=30] -->
+    <string name="user_new_profile_name">New profile</string>
+    <!-- Text shown for title of user info setting [CHAR LIMIT=20]-->
+    <string name="user_info_settings_title">User info</string>
+    <!-- Text shown for title of profile info setting [CHAR LIMIT=20]-->
+    <string name="profile_info_settings_title">Profile info</string>
+    <!-- User settings warning that restricted profile needs a screen lock [CHAR LIMIT=NONE] -->
+    <string name="user_need_lock_message">Before you can create a restricted profile, you\u2019ll need to set up a screen lock to protect your apps and personal data.</string>
+    <!-- User settings dialog button to set screen lock [CHAR LIMIT=25] -->
+    <string name="user_set_lock_button">Set lock</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index b1300a9..922caeb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -66,6 +66,7 @@
     private LocalBluetoothManager mLocalBluetoothManager;
     private InfoMediaManager mInfoMediaManager;
     private String mPackageName;
+    private MediaDevice mOnTransferBluetoothDevice;
 
     @VisibleForTesting
     List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -143,7 +144,7 @@
             final CachedBluetoothDevice cachedDevice =
                     ((BluetoothMediaDevice) device).getCachedDevice();
             if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
-                device.setState(MediaDeviceState.STATE_CONNECTING);
+                mOnTransferBluetoothDevice = connectDevice;
                 cachedDevice.connect();
                 return;
             }
@@ -389,6 +390,10 @@
             mCurrentConnectedDevice = infoMediaDevice != null
                     ? infoMediaDevice : updateCurrentConnectedDevice();
             dispatchDeviceListUpdate();
+            if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
+                connectDevice(mOnTransferBluetoothDevice);
+                mOnTransferBluetoothDevice = null;
+            }
         }
 
         private List<MediaDevice> buildDisconnectedBluetoothDevice() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 6b3a97f..4c61ef5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -107,8 +108,8 @@
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
-                TEST_PACKAGE_NAME);
+        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+                TEST_PACKAGE_NAME));
         mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
                 TEST_PACKAGE_NAME);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
@@ -565,6 +566,34 @@
     }
 
     @Test
+    public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() {
+        final List<MediaDevice> devices = new ArrayList<>();
+        final MediaDevice currentDevice = mock(MediaDevice.class);
+        final MediaDevice device = mock(BluetoothMediaDevice.class);
+        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        mLocalMediaManager.mMediaDevices.add(device);
+        mLocalMediaManager.mMediaDevices.add(currentDevice);
+
+        when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
+        when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
+        when(cachedDevice.isConnected()).thenReturn(false);
+        when(cachedDevice.isBusy()).thenReturn(false);
+
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.connectDevice(device);
+
+        verify(cachedDevice).connect();
+        when(device.isConnected()).thenReturn(true);
+        mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
+        devices.add(mInfoMediaDevice1);
+        devices.add(currentDevice);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+        verify(mInfoMediaDevice1).connect();
+    }
+
+    @Test
     public void onRequestFailed_shouldDispatchOnRequestFailed() {
         mLocalMediaManager.registerCallback(mCallback);
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index da93db7..f2d40ce 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -300,11 +300,6 @@
             android:exported="false"
             android:permission="com.android.systemui.permission.SELF" />
 
-        <service android:name=".assist.AssistHandleService"
-            android:exported="true"
-            android:enabled="false"
-        />
-
         <!-- started from PhoneWindowManager
              TODO: Should have an android:permission attribute -->
         <service android:name=".screenshot.TakeScreenshotService"
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index a06f434..65b04fd 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -19,6 +19,7 @@
     android:id="@+id/bubble_overflow_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:paddingTop="@dimen/bubble_overflow_padding"
     android:orientation="vertical"
     android:layout_gravity="center_horizontal">
 
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
new file mode 100644
index 0000000..d67c81d
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bubble_overflow_view"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <com.android.systemui.bubbles.BadgedImageView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/bubble_view"
+        android:layout_gravity="center"
+        android:layout_width="@dimen/individual_bubble_size"
+        android:layout_height="@dimen/individual_bubble_size"/>
+
+    <TextView
+        android:id="@+id/bubble_view_name"
+        android:fontFamily="@*android:string/config_bodyFontFamily"
+        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+        android:textColor="?android:attr/textColorSecondary"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:maxLines="1"
+        android:layout_gravity="center"
+        android:gravity="center"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 74fc167..4128230 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -50,7 +50,6 @@
     <TextView
         style="@style/TextAppearance.ControlSetup.Subtitle"
         android:id="@+id/controls_subtitle"
-        android:visibility="gone"
         android:layout_marginTop="12dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
new file mode 100644
index 0000000..de9ef21
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+
+<!-- Layout for media controls on the lockscreen -->
+<com.android.systemui.statusbar.notification.stack.MediaHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="0dp"
+    android:paddingEnd="0dp"
+    android:focusable="true"
+    android:clickable="true"
+>
+
+    <!-- Background views required by ActivatableNotificationView. -->
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+        android:id="@+id/backgroundNormal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+    />
+
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+        android:id="@+id/backgroundDimmed"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+    />
+
+    <com.android.systemui.statusbar.notification.FakeShadowView
+        android:id="@+id/fake_shadow"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+    />
+
+    <!-- Layout for media controls. -->
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/keyguard_media_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center"
+        android:padding="16dp"
+    >
+        <ImageView
+            android:id="@+id/album_art"
+            android:layout_width="@dimen/qs_media_album_size"
+            android:layout_height="@dimen/qs_media_album_size"
+            android:layout_marginRight="16dp"
+            android:layout_weight="0"
+        />
+
+        <!-- Media information -->
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+        >
+            <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+            >
+                <com.android.internal.widget.CachingIconView
+                    android:id="@+id/icon"
+                    android:layout_width="16dp"
+                    android:layout_height="16dp"
+                    android:layout_marginEnd="5dp"
+                />
+                <TextView
+                    android:id="@+id/app_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="14sp"
+                    android:singleLine="true"
+                />
+            </LinearLayout>
+
+            <!-- Song name -->
+            <TextView
+                android:id="@+id/header_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+                android:textSize="18sp"
+                android:paddingBottom="6dp"
+                android:gravity="center"/>
+
+            <!-- Artist name -->
+            <TextView
+                android:id="@+id/header_artist"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@*android:string/config_bodyFontFamily"
+                android:textSize="14sp"
+                android:singleLine="true"
+            />
+        </LinearLayout>
+
+        <!-- Controls -->
+        <LinearLayout
+            android:id="@+id/media_actions"
+            android:orientation="horizontal"
+            android:layoutDirection="ltr"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:layout_gravity="center"
+        >
+            <ImageButton
+                style="@android:style/Widget.Material.Button.Borderless.Small"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:gravity="center"
+                android:visibility="gone"
+                android:id="@+id/action0"
+            />
+            <ImageButton
+                style="@android:style/Widget.Material.Button.Borderless.Small"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:gravity="center"
+                android:visibility="gone"
+                android:id="@+id/action1"
+            />
+            <ImageButton
+                style="@android:style/Widget.Material.Button.Borderless.Small"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:gravity="center"
+                android:visibility="gone"
+                android:id="@+id/action2"
+            />
+        </LinearLayout>
+    </LinearLayout>
+
+</com.android.systemui.statusbar.notification.stack.MediaHeaderView>
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index 9ef8c1d..fe8557b 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -56,7 +56,7 @@
             <LinearLayout
                 android:orientation="vertical"
                 android:layout_width="0dp"
-                android:layout_height="@dimen/qs_media_album_size"
+                android:layout_height="wrap_content"
                 android:layout_weight="1"
             >
                 <LinearLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e45cbec..432cd74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1150,8 +1150,8 @@
     <dimen name="bubble_overflow_height">380dp</dimen>
     <!-- Bubble overflow padding when there are no bubbles  -->
     <dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
-    <!-- Margin of overflow bubbles -->
-    <dimen name="bubble_overflow_margin">16dp</dimen>
+    <!-- Padding of container for overflow bubbles -->
+    <dimen name="bubble_overflow_padding">5dp</dimen>
     <!-- Height of the triangle that points to the expanded bubble -->
     <dimen name="bubble_pointer_height">4dp</dimen>
     <!-- Width of the triangle that points to the expanded bubble -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f71c0b3..c8c35c7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2636,14 +2636,17 @@
 
     <!-- Quick Controls strings -->
     <!-- Quick Controls empty state, title [CHAR LIMIT=30] -->
-    <string name="quick_controls_title">Quick Controls</string>
+    <string name="quick_controls_title">Quick controls</string>
     <!-- Quick Controls empty state, subtitle [CHAR LIMIT=100] -->
     <string name="quick_controls_subtitle">Add controls for your connected devices</string>
 
-    <!-- Controls management providers screen title [CHAR LIMIT=30]-->
-    <string name="controls_providers_title">Add Controls</string>
-    <!-- Controls management providers screen subtitle [CHAR LIMIT=NONE] -->
-    <string name="controls_providers_subtitle">Choose app to add controls</string>
+    <!-- Quick Controls setup, title [CHAR LIMIT=50] -->
+    <string name="quick_controls_setup_title">Set up quick controls</string>
+    <!-- Quick Controls setup, subtitle [CHAR LIMIT=100] -->
+    <string name="quick_controls_setup_subtitle">Hold the Power button to access your controls</string>
+
+    <!-- Controls management providers screen title [CHAR LIMIT=60]-->
+    <string name="controls_providers_title">Choose app to add controls</string>
     <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
     <plurals name="controls_number_of_favorites">
         <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item>
@@ -2679,12 +2682,8 @@
     <string name="controls_pin_verifying">Verifying\u2026</string>
     <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] -->
     <string name="controls_pin_instructions">Enter PIN</string>
-    <!-- Controls passphrase entry dialog, text hint [CHAR LIMIT=30] -->
-    <string name="controls_passphrase_instructions">Enter passphrase</string>
     <!-- Controls PIN entry dialog, text hint, retry [CHAR LIMIT=30] -->
     <string name="controls_pin_instructions_retry">Try another PIN</string>
-    <!-- Controls passphrase entry dialog, text hint, retry [CHAR LIMIT=50] -->
-    <string name="controls_passphrase_instructions_retry">Try another passphrase</string>
     <!-- Controls confirmation dialog, waiting to confirm [CHAR LIMIT=30] -->
     <string name="controls_confirmation_confirming">Confirming\u2026</string>
     <!-- Controls confirmation dialog, user prompt [CHAR LIMIT=NONE] -->
@@ -2694,7 +2693,7 @@
     <string name="controls_structure_tooltip">Swipe to see more</string>
 
     <!-- Message to tell the user to wait while systemui attempts to load a set of
-         recommended controls [CHAR_LIMIT=30] -->
+         recommended controls [CHAR_LIMIT=60] -->
     <string name="controls_seeding_in_progress">Loading recommendations</string>
 
     <!-- Close the controls associated with a specific media session [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
new file mode 100644
index 0000000..4fcacc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.MediaMetadata;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.palette.graphics.Palette;
+
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Media controls to display on the lockscreen
+ *
+ * TODO: Should extend MediaControlPanel to avoid code duplication.
+ * Unfortunately, it isn't currently possible because the ActivatableNotificationView background is
+ * different.
+ */
+@Singleton
+public class KeyguardMediaPlayer {
+
+    private static final String TAG = "KeyguardMediaPlayer";
+    // Buttons that can be displayed on lock screen media controls.
+    private static final int[] ACTION_IDS = {R.id.action0, R.id.action1, R.id.action2};
+
+    private final Context mContext;
+    private final Executor mBackgroundExecutor;
+    private float mAlbumArtRadius;
+    private int mAlbumArtSize;
+    private View mMediaNotifView;
+
+    @Inject
+    public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
+        mContext = context;
+        mBackgroundExecutor = backgroundExecutor;
+        loadDimens();
+    }
+
+    /** Binds media controls to a view hierarchy. */
+    public void bindView(View v) {
+        if (mMediaNotifView != null) {
+            throw new IllegalStateException("cannot bind views, already bound");
+        }
+        mMediaNotifView = v;
+        loadDimens();
+    }
+
+    /** Unbinds media controls. */
+    public void unbindView() {
+        if (mMediaNotifView == null) {
+            throw new IllegalStateException("cannot unbind views, nothing bound");
+        }
+        mMediaNotifView = null;
+    }
+
+    /** Clear the media controls because there isn't an active session. */
+    public void clearControls() {
+        if (mMediaNotifView != null) {
+            mMediaNotifView.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Update the media player
+     *
+     * TODO: consider registering a MediaLister instead of exposing this update method.
+     *
+     * @param entry Media notification that will be used to update the player
+     * @param appIcon Icon for the app playing the media
+     * @param mediaMetadata Media metadata that will be used to update the player
+     */
+    public void updateControls(NotificationEntry entry, Icon appIcon,
+            MediaMetadata mediaMetadata) {
+        if (mMediaNotifView == null) {
+            throw new IllegalStateException("cannot update controls, views not bound");
+        }
+        if (mediaMetadata == null) {
+            mMediaNotifView.setVisibility(View.GONE);
+            Log.d(TAG, "media metadata was null");
+            return;
+        }
+        mMediaNotifView.setVisibility(View.VISIBLE);
+
+        Notification notif = entry.getSbn().getNotification();
+
+        // Computed foreground and background color based on album art.
+        int fgColor = notif.color;
+        int bgColor = entry.getRow() == null ? -1 : entry.getRow().getCurrentBackgroundTint();
+        Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+        if (artworkBitmap == null) {
+            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+        }
+        if (artworkBitmap != null) {
+            // If we have art, get colors from that
+            Palette p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
+                    .generate();
+            Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(p);
+            bgColor = swatch.getRgb();
+            fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p);
+        }
+        // Make sure colors will be legible
+        boolean isDark = !ContrastColorUtil.isColorLight(bgColor);
+        fgColor = ContrastColorUtil.resolveContrastColor(mContext, fgColor, bgColor,
+                isDark);
+        fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark);
+
+        // Album art
+        ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
+        if (albumView != null) {
+            // Resize art in a background thread
+            final Bitmap bm = artworkBitmap;
+            mBackgroundExecutor.execute(() -> processAlbumArt(bm, albumView));
+        }
+
+        // App icon
+        ImageView appIconView = mMediaNotifView.findViewById(R.id.icon);
+        if (appIconView != null) {
+            Drawable iconDrawable = appIcon.loadDrawable(mContext);
+            iconDrawable.setTint(fgColor);
+            appIconView.setImageDrawable(iconDrawable);
+        }
+
+        // App name
+        TextView appName = mMediaNotifView.findViewById(R.id.app_name);
+        if (appName != null) {
+            Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
+            String appNameString = builder.loadHeaderAppName();
+            appName.setText(appNameString);
+            appName.setTextColor(fgColor);
+        }
+
+        // Song name
+        TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
+        if (titleText != null) {
+            String songName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+            titleText.setText(songName);
+            titleText.setTextColor(fgColor);
+        }
+
+        // Artist name
+        TextView artistText = mMediaNotifView.findViewById(R.id.header_artist);
+        if (artistText != null) {
+            String artistName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
+            artistText.setText(artistName);
+            artistText.setTextColor(fgColor);
+        }
+
+        // Background color
+        if (mMediaNotifView instanceof MediaHeaderView) {
+            MediaHeaderView head = (MediaHeaderView) mMediaNotifView;
+            head.setBackgroundColor(bgColor);
+        }
+
+        // Control buttons
+        final List<Icon> icons = new ArrayList<>();
+        final List<PendingIntent> intents = new ArrayList<>();
+        Notification.Action[] actions = notif.actions;
+        final int[] actionsToShow = notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+
+        for (int i = 0; i < ACTION_IDS.length; i++) {
+            if (actionsToShow != null && actions != null && i < actionsToShow.length
+                    && actionsToShow[i] < actions.length) {
+                final int idx = actionsToShow[i];
+                icons.add(actions[idx].getIcon());
+                intents.add(actions[idx].actionIntent);
+            } else {
+                icons.add(null);
+                intents.add(null);
+            }
+        }
+
+        Context packageContext = entry.getSbn().getPackageContext(mContext);
+        for (int i = 0; i < ACTION_IDS.length; i++) {
+            ImageButton button = mMediaNotifView.findViewById(ACTION_IDS[i]);
+            if (button == null) {
+                continue;
+            }
+            Icon icon = icons.get(i);
+            if (icon == null) {
+                button.setVisibility(View.GONE);
+            } else {
+                button.setVisibility(View.VISIBLE);
+                button.setImageDrawable(icon.loadDrawable(packageContext));
+                button.setImageTintList(ColorStateList.valueOf(fgColor));
+                final PendingIntent intent = intents.get(i);
+                if (intent != null) {
+                    button.setOnClickListener(v -> {
+                        try {
+                            intent.send();
+                        } catch (PendingIntent.CanceledException e) {
+                            Log.d(TAG, "failed to send action intent", e);
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    /**
+     * Process album art for layout
+     * @param albumArt bitmap to use for album art
+     * @param albumView view to hold the album art
+     */
+    private void processAlbumArt(Bitmap albumArt, ImageView albumView) {
+        RoundedBitmapDrawable roundedDrawable = null;
+        if (albumArt != null) {
+            Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
+            Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
+                    false);
+            roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
+            roundedDrawable.setCornerRadius(mAlbumArtRadius);
+        } else {
+            Log.e(TAG, "No album art available");
+        }
+
+        // Now that it's resized, update the UI
+        final RoundedBitmapDrawable result = roundedDrawable;
+        albumView.post(() -> {
+            albumView.setImageDrawable(result);
+            albumView.setVisibility(result == null ? View.GONE : View.VISIBLE);
+        });
+    }
+
+    private void loadDimens() {
+        mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+        mAlbumArtSize = (int) mContext.getResources().getDimension(
+                    R.dimen.qs_media_album_size);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 18357a9..3afe19f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -966,17 +966,20 @@
         boolean changed = false;
 
         if (enabled && (oldIntent == null)) {
-            Intent intent = new Intent(
-                    DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE);
-            ComponentName profileOwnerComponent =
-                    mDevicePolicyManager.getProfileOwnerAsUser(userId);
-            intent.setComponent(profileOwnerComponent);
-            ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 0);
-            if (resolveInfo != null) {
-                Intent newIntent = new Intent();
-                newIntent.setComponent(profileOwnerComponent);
-                mSecondaryLockscreenRequirement.put(userId, newIntent);
-                changed = true;
+            ComponentName poComponent = mDevicePolicyManager.getProfileOwnerAsUser(userId);
+            if (poComponent == null) {
+                Log.e(TAG, "No profile owner found for User " + userId);
+            } else {
+                Intent intent =
+                        new Intent(DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE)
+                                .setPackage(poComponent.getPackageName());
+                ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 0);
+                if (resolveInfo != null && resolveInfo.serviceInfo != null) {
+                    Intent launchIntent =
+                            new Intent().setComponent(resolveInfo.serviceInfo.getComponentName());
+                    mSecondaryLockscreenRequirement.put(userId, launchIntent);
+                    changed = true;
+                }
             }
         } else if (!enabled && (oldIntent != null)) {
             mSecondaryLockscreenRequirement.put(userId, null);
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index e2b12da..59af458 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -86,7 +86,8 @@
     private float mInitialTouchSpan;
     private float mLastFocusY;
     private float mLastSpanY;
-    private int mTouchSlop;
+    private final int mTouchSlop;
+    private final float mSlopMultiplier;
     private float mLastMotionY;
     private float mPullGestureMinXSpan;
     private Callback mCallback;
@@ -177,6 +178,7 @@
 
         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
         mTouchSlop = configuration.getScaledTouchSlop();
+        mSlopMultiplier = configuration.getAmbiguousGestureMultiplier();
 
         mSGD = new ScaleGestureDetector(context, mScaleGestureListener);
         mFlingAnimationUtils = new FlingAnimationUtils(mContext.getResources().getDisplayMetrics(),
@@ -258,6 +260,13 @@
         mScrollAdapter = adapter;
     }
 
+    private float getTouchSlop(MotionEvent event) {
+        // Adjust the touch slop if another gesture may be being performed.
+        return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                ? mTouchSlop * mSlopMultiplier
+                : mTouchSlop;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (!isEnabled()) {
@@ -303,7 +312,7 @@
                 if (mWatchingForPull) {
                     final float yDiff = ev.getRawY() - mInitialTouchY;
                     final float xDiff = ev.getRawX() - mInitialTouchX;
-                    if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
+                    if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) {
                         if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
                         mWatchingForPull = false;
                         if (mResizedView != null && !isFullyExpanded(mResizedView)) {
@@ -431,7 +440,7 @@
                 if (mWatchingForPull) {
                     final float yDiff = ev.getRawY() - mInitialTouchY;
                     final float xDiff = ev.getRawX() - mInitialTouchX;
-                    if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
+                    if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) {
                         if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
                         mWatchingForPull = false;
                         if (mResizedView != null && !isFullyExpanded(mResizedView)) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5f004a6..281b566 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -33,6 +33,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.Dimension;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -122,7 +123,8 @@
     protected int mRoundedDefaultBottom;
     @VisibleForTesting
     protected View[] mOverlays;
-    private DisplayCutoutView[] mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
+    @Nullable
+    private DisplayCutoutView[] mCutoutViews;
     private float mDensity;
     private WindowManager mWindowManager;
     private int mRotation;
@@ -135,18 +137,32 @@
             new CameraAvailabilityListener.CameraTransitionCallback() {
         @Override
         public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
+            if (mCutoutViews == null) {
+                Log.w(TAG, "DisplayCutoutView do not initialized");
+                return;
+            }
             // Show the extra protection around the front facing camera if necessary
             for (DisplayCutoutView dcv : mCutoutViews) {
-                dcv.setProtection(protectionPath, bounds);
-                dcv.setShowProtection(true);
+                // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
+                if (dcv != null) {
+                    dcv.setProtection(protectionPath, bounds);
+                    dcv.setShowProtection(true);
+                }
             }
         }
 
         @Override
         public void onHideCameraProtection() {
+            if (mCutoutViews == null) {
+                Log.w(TAG, "DisplayCutoutView do not initialized");
+                return;
+            }
             // Go back to the regular anti-aliasing
             for (DisplayCutoutView dcv : mCutoutViews) {
-                dcv.setShowProtection(false);
+                // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
+                if (dcv != null) {
+                    dcv.setShowProtection(false);
+                }
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 02a4521..d17ca404 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -69,6 +69,7 @@
 
     private final FlingAnimationUtils mFlingAnimationUtils;
     private float mPagingTouchSlop;
+    private final float mSlopMultiplier;
     private final Callback mCallback;
     private final int mSwipeDirection;
     private final VelocityTracker mVelocityTracker;
@@ -84,11 +85,28 @@
     private float mTranslation = 0;
 
     private boolean mMenuRowIntercepting;
-    private boolean mLongPressSent;
-    private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
+    private boolean mLongPressSent;
+    private final float[] mDownLocation = new float[2];
+    private final Runnable mPerformLongPress = new Runnable() {
 
-    final private int[] mTmpPos = new int[2];
+        private final int[] mViewOffset = new int[2];
+
+        @Override
+        public void run() {
+            if (mCurrView != null && !mLongPressSent) {
+                mLongPressSent = true;
+                if (mCurrView instanceof ExpandableNotificationRow) {
+                    mCurrView.getLocationOnScreen(mViewOffset);
+                    final int x = (int) mDownLocation[0] - mViewOffset[0];
+                    final int y = (int) mDownLocation[1] - mViewOffset[1];
+                    mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                    ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y);
+                }
+            }
+        }
+    };
+
     private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
     private boolean mDisableHwLayers;
@@ -102,7 +120,9 @@
         mHandler = new Handler();
         mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
-        mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
 
         // Extra long-press!
         mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
@@ -255,10 +275,7 @@
     }
 
     public void cancelLongPress() {
-        if (mWatchLongPress != null) {
-            mHandler.removeCallbacks(mWatchLongPress);
-            mWatchLongPress = null;
-        }
+        mHandler.removeCallbacks(mPerformLongPress);
     }
 
     @Override
@@ -287,27 +304,9 @@
                     mInitialTouchPos = getPos(ev);
                     mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
                     mTranslation = getTranslation(mCurrView);
-                    if (mWatchLongPress == null) {
-                        mWatchLongPress = new Runnable() {
-                            @Override
-                            public void run() {
-                                if (mCurrView != null && !mLongPressSent) {
-                                    mLongPressSent = true;
-                                    mCurrView.getLocationOnScreen(mTmpPos);
-                                    final int x = (int) ev.getRawX() - mTmpPos[0];
-                                    final int y = (int) ev.getRawY() - mTmpPos[1];
-                                    if (mCurrView instanceof ExpandableNotificationRow) {
-                                        mCurrView.sendAccessibilityEvent(
-                                                AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                                        ExpandableNotificationRow currRow =
-                                                (ExpandableNotificationRow) mCurrView;
-                                        currRow.doLongClickCallback(x, y);
-                                    }
-                                }
-                            }
-                        };
-                    }
-                    mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
+                    mDownLocation[0] = ev.getRawX();
+                    mDownLocation[1] = ev.getRawY();
+                    mHandler.postDelayed(mPerformLongPress, mLongPressTimeout);
                 }
                 break;
 
@@ -318,7 +317,12 @@
                     float perpendicularPos = getPerpendicularPos(ev);
                     float delta = pos - mInitialTouchPos;
                     float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos;
-                    if (Math.abs(delta) > mPagingTouchSlop
+                    // Adjust the touch slop if another gesture may be being performed.
+                    final float pagingTouchSlop =
+                            ev.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                            ? mPagingTouchSlop * mSlopMultiplier
+                            : mPagingTouchSlop;
+                    if (Math.abs(delta) > pagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
                         if (mCallback.canChildBeDragged(mCurrView)) {
                             mCallback.onBeginDrag(mCurrView);
@@ -327,6 +331,11 @@
                             mTranslation = getTranslation(mCurrView);
                         }
                         cancelLongPress();
+                    } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS
+                                    && mHandler.hasCallbacks(mPerformLongPress)) {
+                        // Accelerate the long press signal.
+                        cancelLongPress();
+                        mPerformLongPress.run();
                     }
                 }
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 3707d61..8cd89dd 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.Nullable;
 
@@ -45,6 +46,8 @@
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
+import dagger.Lazy;
+
 /**
  * A class for managing Assistant handle logic.
  *
@@ -73,6 +76,7 @@
     private final Provider<AssistHandleViewController> mAssistHandleViewController;
     private final DeviceConfigHelper mDeviceConfigHelper;
     private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
+    private final Lazy<AccessibilityManager> mA11yManager;
 
     private boolean mHandlesShowing = false;
     private long mHandlesLastHiddenAt;
@@ -93,6 +97,7 @@
             DeviceConfigHelper deviceConfigHelper,
             Map<AssistHandleBehavior, BehaviorController> behaviorMap,
             NavigationModeController navigationModeController,
+            Lazy<AccessibilityManager> a11yManager,
             DumpManager dumpManager) {
         mContext = context;
         mAssistUtils = assistUtils;
@@ -100,6 +105,7 @@
         mAssistHandleViewController = assistHandleViewController;
         mDeviceConfigHelper = deviceConfigHelper;
         mBehaviorMap = behaviorMap;
+        mA11yManager = a11yManager;
 
         mInGesturalMode = QuickStepContract.isGesturalMode(
                 navigationModeController.addListener(this::handleNavigationModeChange));
@@ -211,9 +217,11 @@
     }
 
     private long getShowAndGoDuration() {
-        return mDeviceConfigHelper.getLong(
+        long configuredTime = mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
                 DEFAULT_SHOW_AND_GO_DURATION_MS);
+        return mA11yManager.get().getRecommendedTimeoutMillis(
+                (int) configuredTime, AccessibilityManager.FLAG_CONTENT_ICONS);
     }
 
     private String getBehaviorMode() {
@@ -291,7 +299,7 @@
 
         pw.println("   Phenotype Flags:");
         pw.println("      "
-                + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS
+                + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS + "(a11y modded)"
                 + "="
                 + getShowAndGoDuration());
         pw.println("      "
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
deleted file mode 100644
index 9ceafc6..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.assist
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import dagger.Lazy
-import javax.inject.Inject
-
-class AssistHandleService @Inject constructor(private val assistManager: Lazy<AssistManager>)
-    : Service() {
-
-    private val binder = object : IAssistHandleService.Stub() {
-        override fun requestAssistHandles() {
-            assistManager.get().requestAssistHandles()
-        }
-    }
-
-    override fun onBind(intent: Intent?): IBinder? {
-        return binder
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 96939b01..6f5a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.assist;
 
-import android.app.Service;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -34,11 +33,8 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
 
 /** Module for dagger injections related to the Assistant. */
 @Module
@@ -91,9 +87,4 @@
     static Clock provideSystemClock() {
         return SystemClock::uptimeMillis;
     }
-
-    @Binds
-    @IntoMap
-    @ClassKey(AssistHandleService.class)
-    abstract Service bindAssistHandleService(AssistHandleService assistHandleService);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c9ce8a1..31f90c6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -957,9 +957,10 @@
             String key = orderedKeys[i];
             NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
             rankingMap.getRanking(key, mTmpRanking);
-            if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+            boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+            if (isActiveBubble && !mTmpRanking.canBubble()) {
                 mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
-            } else if (entry != null && mTmpRanking.isBubble()) {
+            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
                 entry.setFlagBubble(true);
                 onEntryUpdated(entry);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index fcbd912..8bc35f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -233,6 +233,7 @@
                 Bubble b = mOverflowBubbles.get(i);
                 if (b.getKey().equals(entry.getKey())) {
                     moveOverflowBubbleToPending(b);
+                    b.setEntry(entry);
                     return b;
                 }
             }
@@ -240,6 +241,7 @@
             for (int i = 0; i < mPendingBubbles.size(); i++) {
                 Bubble b = mPendingBubbles.get(i);
                 if (b.getKey().equals(entry.getKey())) {
+                    b.setEntry(entry);
                     return b;
                 }
             }
@@ -403,6 +405,9 @@
     }
 
     private void doRemove(String key, @DismissReason int reason) {
+        if (DEBUG_BUBBLE_DATA) {
+            Log.d(TAG, "doRemove: " + key);
+        }
         //  If it was pending remove it
         for (int i = 0; i < mPendingBubbles.size(); i++) {
             if (mPendingBubbles.get(i).getKey().equals(key)) {
@@ -445,15 +450,14 @@
         if (reason == BubbleController.DISMISS_AGED
                 || reason == BubbleController.DISMISS_USER_GESTURE) {
             if (DEBUG_BUBBLE_DATA) {
-                Log.d(TAG, "overflowing bubble: " + bubble);
+                Log.d(TAG, "Overflowing: " + bubble);
             }
             mOverflowBubbles.add(0, bubble);
             bubble.stopInflation();
-
             if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
                 // Remove oldest bubble.
                 if (DEBUG_BUBBLE_DATA) {
-                    Log.d(TAG, "Overflow full. Remove bubble: " + mOverflowBubbles.get(
+                    Log.d(TAG, "Overflow full. Remove: " + mOverflowBubbles.get(
                             mOverflowBubbles.size() - 1));
                 }
                 mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 7636c67..b651985 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -21,14 +21,20 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.Person;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -67,13 +73,22 @@
 
         mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
         mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
-        mRecyclerView.setLayoutManager(
-                new GridLayoutManager(getApplicationContext(),
-                        getResources().getInteger(R.integer.bubbles_overflow_columns)));
 
-        int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin);
+        Resources res = getResources();
+        final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
+        mRecyclerView.setLayoutManager(
+                new GridLayoutManager(getApplicationContext(), columns));
+
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+        final int viewWidth = displayMetrics.widthPixels / columns;
+
+        final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow);
+        final int rows = (int) Math.ceil((double) maxOverflowBubbles / columns);
+        final int viewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height) / rows;
+
         mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
-                mBubbleController::promoteBubbleFromOverflow, bubbleMargin);
+                mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
         mRecyclerView.setAdapter(mAdapter);
         onDataChanged(mBubbleController.getOverflowBubbles());
         mBubbleController.setOverflowCallback(() -> {
@@ -139,39 +154,48 @@
 class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
     private Consumer<Bubble> mPromoteBubbleFromOverflow;
     private List<Bubble> mBubbles;
-    private int mBubbleMargin;
+    private int mWidth;
+    private int mHeight;
 
-    public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble,
-            int bubbleMargin) {
+    public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
+            int height) {
         mBubbles = list;
         mPromoteBubbleFromOverflow = promoteBubble;
-        mBubbleMargin = bubbleMargin;
+        mWidth = width;
+        mHeight = height;
     }
 
     @Override
     public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
             int viewType) {
-        BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.bubble_view, parent, false);
+        LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.bubble_overflow_view, parent, false);
         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                 LinearLayout.LayoutParams.WRAP_CONTENT,
-                LinearLayout.LayoutParams.WRAP_CONTENT
-        );
-        params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin);
-        view.setLayoutParams(params);
-        return new ViewHolder(view);
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        params.width = mWidth;
+        params.height = mHeight;
+        overflowView.setLayoutParams(params);
+        return new ViewHolder(overflowView);
     }
 
     @Override
     public void onBindViewHolder(ViewHolder vh, int index) {
-        Bubble bubble = mBubbles.get(index);
+        Bubble b = mBubbles.get(index);
 
-        vh.mBadgedImageView.update(bubble);
-        vh.mBadgedImageView.setOnClickListener(view -> {
-            mBubbles.remove(bubble);
+        vh.iconView.update(b);
+        vh.iconView.setOnClickListener(view -> {
+            mBubbles.remove(b);
             notifyDataSetChanged();
-            mPromoteBubbleFromOverflow.accept(bubble);
+            mPromoteBubbleFromOverflow.accept(b);
         });
+
+        Bubble.FlyoutMessage message = b.getFlyoutMessage();
+        if (message != null && message.senderName != null) {
+            vh.textView.setText(message.senderName);
+        } else {
+            vh.textView.setText(b.getAppName());
+        }
     }
 
     @Override
@@ -180,11 +204,13 @@
     }
 
     public static class ViewHolder extends RecyclerView.ViewHolder {
-        public BadgedImageView mBadgedImageView;
+        public BadgedImageView iconView;
+        public TextView textView;
 
-        public ViewHolder(BadgedImageView v) {
+        public ViewHolder(LinearLayout v) {
             super(v);
-            mBadgedImageView = v;
+            iconView = v.findViewById(R.id.bubble_view);
+            textView = v.findViewById(R.id.bubble_view_name);
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cff371f..541c8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -940,7 +940,6 @@
         ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
         animateInFlyoutForBubble(bubble);
         updatePointerPosition();
-        updateOverflowBtnVisibility( /*apply */ true);
         requestUpdate();
         logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
@@ -951,16 +950,18 @@
             Log.d(TAG, "removeBubble: " + bubble);
         }
         // Remove it from the views
-        int removedIndex = mBubbleContainer.indexOfChild(bubble.getIconView());
-        if (removedIndex >= 0) {
-            mBubbleContainer.removeViewAt(removedIndex);
-            bubble.cleanupExpandedState();
-            bubble.setInflated(false);
-            logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
-        } else {
-            Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
+        for (int i = 0; i < getBubbleCount(); i++) {
+            View v = mBubbleContainer.getChildAt(i);
+            if (v instanceof BadgedImageView
+                    && ((BadgedImageView) v).getKey().equals(bubble.getKey())) {
+                mBubbleContainer.removeViewAt(i);
+                bubble.cleanupExpandedState();
+                bubble.setInflated(false);
+                logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+                return;
+            }
         }
-        updateOverflowBtnVisibility(/* apply */ true);
+        Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
     }
 
     private void updateOverflowBtnVisibility(boolean apply) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 3fd583f..11181e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -65,11 +65,18 @@
     override val elements: List<ElementWrapper> = createWrappers(controls)
 
     override fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+        val toChange = elements.firstOrNull {
+            it is ControlWrapper && it.controlStatus.control.controlId == controlId
+        } as ControlWrapper?
+        if (favorite == toChange?.controlStatus?.favorite) return
         if (favorite) {
             favoriteIds.add(controlId)
         } else {
             favoriteIds.remove(controlId)
         }
+        toChange?.let {
+            it.controlStatus.favorite = favorite
+        }
     }
 
     private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 098caf6..0c41f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -84,8 +84,6 @@
 
         requireViewById<TextView>(R.id.title).text =
                 resources.getText(R.string.controls_providers_title)
-        requireViewById<TextView>(R.id.subtitle).text =
-                resources.getText(R.string.controls_providers_subtitle)
 
         requireViewById<Button>(R.id.done).setOnClickListener {
             this@ControlsProviderSelectorActivity.finishAffinity()
@@ -117,4 +115,4 @@
         currentUserTracker.stopTracking()
         super.onDestroy()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 34dcca2..b0db437 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -172,7 +172,7 @@
         val inflater = LayoutInflater.from(context)
         inflater.inflate(R.layout.controls_no_favorites, parent, true)
         val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
-        subtitle.setVisibility(View.VISIBLE)
+        subtitle.setText(context.resources.getString(R.string.controls_seeding_in_progress))
     }
 
     private fun showInitialSetupView(items: List<SelectionItem>) {
@@ -184,6 +184,9 @@
         val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
         viewGroup.setOnClickListener(launchSelectorActivityListener(context))
 
+        val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
+        subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle))
+
         val iconRowGroup = parent.requireViewById(R.id.controls_icon_row) as ViewGroup
         items.forEach {
             val imageView = inflater.inflate(R.layout.controls_icon, viewGroup, false) as ImageView
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 09d7d26..b329991 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -490,6 +490,9 @@
         public void onPowerSaveChanged(boolean active) {
             if (mDozeHost.isPowerSaveActive()) {
                 mMachine.requestState(DozeMachine.State.DOZE);
+            } else if (mMachine.getState() == DozeMachine.State.DOZE
+                    && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+                mMachine.requestState(DozeMachine.State.DOZE_AOD);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 73539f9..6514ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1849,7 +1849,7 @@
                     .alpha(1)
                     .translationX(0)
                     .translationY(0)
-                    .setDuration(300)
+                    .setDuration(450)
                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .setUpdateListener(animation -> {
                         float animatedValue = animation.getAnimatedFraction();
@@ -1878,7 +1878,7 @@
                     .alpha(0)
                     .translationX(mGlobalActionsLayout.getAnimationOffsetX())
                     .translationY(mGlobalActionsLayout.getAnimationOffsetY())
-                    .setDuration(300)
+                    .setDuration(550)
                     .withEndAction(this::completeDismiss)
                     .setInterpolator(new LogAccelerateInterpolator())
                     .setUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index c287055..6ce5e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -217,7 +217,7 @@
     }
 
     @Override
-    public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+    public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                 getAspectRatioOrDefault(info.pictureInPictureParams),
@@ -250,7 +250,7 @@
     }
 
     @Override
-    public void taskVanished(ActivityManager.RunningTaskInfo info) {
+    public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
         IWindowContainer token = info.token;
         Objects.requireNonNull(token, "Requires valid IWindowContainer");
         if (token.asBinder() != mToken.asBinder()) {
@@ -346,6 +346,7 @@
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
+                .resetScale(tx, mLeash, destinationBounds)
                 .round(tx, mLeash, mInPip);
         scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 014f3b5..1a01cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -208,7 +208,8 @@
                     final Rect currentPipBounds = mMotionHelper.getBounds();
                     mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
                             mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
-                            mMinSize.y, mMaxSize, true, true));
+                            mMinSize.y, mMaxSize, true,
+                            mLastDownBounds.width() > mLastDownBounds.height()));
                     mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
                     mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
                             null);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
index 3cdc01d..dea8c32 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
@@ -38,7 +38,7 @@
         @Override
         protected void onInactive() {
             super.onInactive();
-            mTracker.startTracking();
+            mTracker.stopTracking();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 131f4e1..3879c16 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -339,7 +339,6 @@
             insets = state.calculateInsets(state.getDisplayFrame(),
                     null /* ignoringVisibilityState */, insets.isRound(),
                     insets.shouldAlwaysConsumeSystemBars(), insets.getDisplayCutout(),
-                    null /* legacyContentInsets */, null /* legacyStableInsets */,
                     0 /* legacySystemUiFlags */,
                     SOFT_INPUT_ADJUST_NOTHING, null /* typeSideMap */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 8bbb548..c4089e5 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -82,11 +82,11 @@
     }
 
     @Override
-    public void taskAppeared(RunningTaskInfo taskInfo) {
+    public void onTaskAppeared(RunningTaskInfo taskInfo) {
     }
 
     @Override
-    public void taskVanished(RunningTaskInfo taskInfo) {
+    public void onTaskVanished(RunningTaskInfo taskInfo) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 5adee40..4fa7822 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -48,7 +48,8 @@
     private float mInitialTouchX;
     private float mInitialTouchY;
     private boolean mDraggingDown;
-    private float mTouchSlop;
+    private final float mTouchSlop;
+    private final float mSlopMultiplier;
     private DragDownCallback mDragDownCallback;
     private View mHost;
     private final int[] mTemp2 = new int[2];
@@ -62,7 +63,9 @@
             FalsingManager falsingManager) {
         mMinDragDistance = context.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_drag_down_min_distance);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
         mCallback = callback;
         mDragDownCallback = dragDownCallback;
         mHost = host;
@@ -85,7 +88,12 @@
 
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
-                if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
+                // Adjust the touch slop if another gesture may be being performed.
+                final float touchSlop =
+                        event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                        ? mTouchSlop * mSlopMultiplier
+                        : mTouchSlop;
+                if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) {
                     mFalsingManager.onNotificatonStartDraggingDown();
                     mDraggingDown = true;
                     captureStartingChild(mInitialTouchX, mInitialTouchY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d8fdf92..e32d174 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -46,6 +46,7 @@
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.keyguard.KeyguardMediaPlayer;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
@@ -65,6 +66,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.Utils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -111,6 +113,7 @@
     private ScrimController mScrimController;
     @Nullable
     private LockscreenWallpaper mLockscreenWallpaper;
+    private final KeyguardMediaPlayer mMediaPlayer;
 
     private final Executor mMainExecutor;
 
@@ -184,11 +187,13 @@
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
             KeyguardBypassController keyguardBypassController,
+            KeyguardMediaPlayer keyguardMediaPlayer,
             @Main Executor mainExecutor,
             DeviceConfigProxy deviceConfig) {
         mContext = context;
         mMediaArtworkProcessor = mediaArtworkProcessor;
         mKeyguardBypassController = keyguardBypassController;
+        mMediaPlayer = keyguardMediaPlayer;
         mMediaListeners = new ArrayList<>();
         // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
         // in session state
@@ -468,6 +473,7 @@
             && mBiometricUnlockController.isWakeAndUnlock();
         if (mKeyguardStateController.isLaunchTransitionFadingAway() || wakeAndUnlock) {
             mBackdrop.setVisibility(View.INVISIBLE);
+            mMediaPlayer.clearControls();
             Trace.endSection();
             return;
         }
@@ -490,6 +496,14 @@
             }
         }
 
+        NotificationEntry entry = mEntryManager
+                .getActiveNotificationUnfiltered(mMediaNotificationKey);
+        if (entry != null) {
+            mMediaPlayer.updateControls(entry, getMediaIcon(), mediaMetadata);
+        } else {
+            mMediaPlayer.clearControls();
+        }
+
         // Process artwork on a background thread and send the resulting bitmap to
         // finishUpdateMediaMetaData.
         if (metaDataChanged) {
@@ -498,7 +512,7 @@
             }
             mProcessArtworkTasks.clear();
         }
-        if (artworkBitmap != null) {
+        if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
             mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
                     allowEnterAnimation).execute(artworkBitmap));
         } else {
@@ -612,6 +626,7 @@
                     // We are unlocking directly - no animation!
                     mBackdrop.setVisibility(View.GONE);
                     mBackdropBack.setImageDrawable(null);
+                    mMediaPlayer.clearControls();
                     if (windowController != null) {
                         windowController.setBackdropShowing(false);
                     }
@@ -628,6 +643,7 @@
                                 mBackdrop.setVisibility(View.GONE);
                                 mBackdropFront.animate().cancel();
                                 mBackdropBack.setImageDrawable(null);
+                                mMediaPlayer.clearControls();
                                 mMainExecutor.execute(mHideBackdropFront);
                             });
                     if (mKeyguardStateController.isKeyguardFadingAway()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8945f36..a3faa80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -67,32 +67,9 @@
     private var updateScheduled: Boolean = false
     private var shadeExpansion = 0f
     @VisibleForTesting
-    var shadeSpring = SpringAnimation(this, object :
-            FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
-        override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
-            shadeBlurRadius = value.toInt()
-        }
-
-        override fun getValue(rect: NotificationShadeDepthController?): Float {
-            return shadeBlurRadius.toFloat()
-        }
-    })
-    private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
-
-    /**
-     * Radius that we're animating to.
-     */
-    private var pendingShadeBlurRadius = -1
-
-    /**
-     * Shade blur radius on the current frame.
-     */
-    private var shadeBlurRadius = 0
-        set(value) {
-            if (field == value) return
-            field = value
-            scheduleUpdate()
-        }
+    var shadeSpring = DepthAnimation()
+    @VisibleForTesting
+    var globalActionsSpring = DepthAnimation()
 
     /**
      * Blur radius of the wake-up animation on this frame.
@@ -103,7 +80,6 @@
             field = value
             scheduleUpdate()
         }
-    private var globalDialogVisibility = 0f
 
     /**
      * Callback that updates the window blur value and is called only once per frame.
@@ -111,12 +87,9 @@
     private val updateBlurCallback = Choreographer.FrameCallback {
         updateScheduled = false
 
-        val blur = max(shadeBlurRadius,
-                max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility)))
+        val blur = max(max(shadeSpring.radius, wakeAndUnlockBlurRadius), globalActionsSpring.radius)
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
-        val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
-        wallpaperManager.setWallpaperZoomOut(root.windowToken,
-                zoomInterpolator.getInterpolation(rawZoom))
+        wallpaperManager.setWallpaperZoomOut(root.windowToken, blurUtils.ratioOfBlurRadius(blur))
         notificationShadeWindowController.setBackgroundBlurRadius(blur)
     }
 
@@ -163,8 +136,9 @@
         }
 
         override fun onDozingChanged(isDozing: Boolean) {
-            if (isDozing && shadeSpring.isRunning) {
-                shadeSpring.skipToEnd()
+            if (isDozing) {
+                shadeSpring.finishIfRunning()
+                globalActionsSpring.finishIfRunning()
             }
         }
     }
@@ -174,10 +148,6 @@
         if (WAKE_UP_ANIMATION_ENABLED) {
             keyguardStateController.addCallback(keyguardStateCallback)
         }
-        shadeSpring.spring = SpringForce(0.0f)
-        shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-        shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
-        shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
         statusBarStateController.addCallback(statusBarStateCallback)
     }
 
@@ -198,11 +168,7 @@
             newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
         }
 
-        if (pendingShadeBlurRadius == newBlur) {
-            return
-        }
-        pendingShadeBlurRadius = newBlur
-        shadeSpring.animateToFinalPosition(newBlur.toFloat())
+        shadeSpring.animateTo(newBlur)
     }
 
     private fun scheduleUpdate(viewToBlur: View? = null) {
@@ -215,19 +181,72 @@
     }
 
     fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) {
-        if (visibility == globalDialogVisibility) {
-            return
-        }
-        globalDialogVisibility = visibility
-        scheduleUpdate(dialogView)
+        globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         IndentingPrintWriter(pw, "  ").let {
             it.println("StatusBarWindowBlurController:")
             it.increaseIndent()
-            it.println("shadeBlurRadius: $shadeBlurRadius")
+            it.println("shadeRadius: ${shadeSpring.radius}")
+            it.println("globalActionsRadius: ${globalActionsSpring.radius}")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
         }
     }
+
+    /**
+     * Animation helper that smoothly animates the depth using a spring and deals with frame
+     * invalidation.
+     */
+    inner class DepthAnimation() {
+        /**
+         * Blur radius visible on the UI, in pixels.
+         */
+        var radius = 0
+            private set
+
+        /**
+         * Radius that we're animating to.
+         */
+        private var pendingRadius = -1
+
+        /**
+         * View on {@link Surface} that wants depth.
+         */
+        private var view: View? = null
+
+        private var springAnimation = SpringAnimation(this, object :
+            FloatPropertyCompat<DepthAnimation>("blurRadius") {
+            override fun setValue(rect: DepthAnimation?, value: Float) {
+                radius = value.toInt()
+                scheduleUpdate(view)
+            }
+
+            override fun getValue(rect: DepthAnimation?): Float {
+                return radius.toFloat()
+            }
+        })
+
+        init {
+            springAnimation.spring = SpringForce(0.0f)
+            springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+            springAnimation.spring.stiffness = SpringForce.STIFFNESS_MEDIUM
+            springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
+        }
+
+        fun animateTo(newRadius: Int, viewToBlur: View? = null) {
+            if (pendingRadius == newRadius && view == viewToBlur) {
+                return
+            }
+            view = viewToBlur
+            pendingRadius = newRadius
+            springAnimation.animateToFinalPosition(newRadius.toFloat())
+        }
+
+        fun finishIfRunning() {
+            if (springAnimation.isRunning) {
+                springAnimation.skipToEnd()
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 4c99a90..e64b423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardMediaPlayer;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -93,6 +94,7 @@
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
             KeyguardBypassController keyguardBypassController,
+            KeyguardMediaPlayer keyguardMediaPlayer,
             @Main Executor mainExecutor,
             DeviceConfigProxy deviceConfigProxy) {
         return new NotificationMediaManager(
@@ -102,6 +104,7 @@
                 notificationEntryManager,
                 mediaArtworkProcessor,
                 keyguardBypassController,
+                keyguardMediaPlayer,
                 mainExecutor,
                 deviceConfigProxy);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 5d1ab4f..db54586 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -152,7 +152,13 @@
         }
     }
 
-    private int selectForegroundColor(int backgroundColor, Palette palette) {
+    /**
+     * Select a foreground color depending on whether the background color is dark or light
+     * @param backgroundColor Background color to coordinate with
+     * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
+     * @return foreground color
+     */
+    public static int selectForegroundColor(int backgroundColor, Palette palette) {
         if (ContrastColorUtil.isColorLight(backgroundColor)) {
             return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
                     palette.getVibrantSwatch(),
@@ -170,7 +176,7 @@
         }
     }
 
-    private int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
+    private static int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
             Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
             Palette.Swatch dominantSwatch, int fallbackColor) {
         Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
@@ -194,7 +200,7 @@
         }
     }
 
-    private Palette.Swatch selectMutedCandidate(Palette.Swatch first,
+    private static Palette.Swatch selectMutedCandidate(Palette.Swatch first,
             Palette.Swatch second) {
         boolean firstValid = hasEnoughPopulation(first);
         boolean secondValid = hasEnoughPopulation(second);
@@ -215,7 +221,8 @@
         return null;
     }
 
-    private Palette.Swatch selectVibrantCandidate(Palette.Swatch first, Palette.Swatch second) {
+    private static Palette.Swatch selectVibrantCandidate(Palette.Swatch first,
+            Palette.Swatch second) {
         boolean firstValid = hasEnoughPopulation(first);
         boolean secondValid = hasEnoughPopulation(second);
         if (firstValid && secondValid) {
@@ -235,7 +242,7 @@
         return null;
     }
 
-    private boolean hasEnoughPopulation(Palette.Swatch swatch) {
+    private static boolean hasEnoughPopulation(Palette.Swatch swatch) {
         // We want a fraction that is at least 1% of the image
         return swatch != null
                 && (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
@@ -257,7 +264,7 @@
      * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
      * @return Swatch that should be used as the background of the media notification.
      */
-    private static Palette.Swatch findBackgroundSwatch(Palette palette) {
+    public static Palette.Swatch findBackgroundSwatch(Palette palette) {
         // by default we use the dominant palette
         Palette.Swatch dominantSwatch = palette.getDominantSwatch();
         if (dominantSwatch == null) {
@@ -301,7 +308,7 @@
      * @param artwork Media artwork
      * @return Builder that generates the {@link Palette} for the media artwork.
      */
-    private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
+    public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
         // for the background we only take the left side of the image to ensure
         // a smooth transition
         return Palette.from(artwork)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c6d84ff..d2f781d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -651,7 +651,7 @@
      */
     public void updateNotifications(String reason) {
         reapplyFilterAndSort(reason);
-        if (mPresenter != null) {
+        if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
             mPresenter.updateNotificationViews();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index 48386dc..e2b01ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -23,9 +23,11 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
 import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.Utils
 
 import javax.inject.Inject
 
@@ -43,9 +45,18 @@
         return usePeopleFiltering(proxy)
     }
 
+    fun isMediaControlsEnabled(): Boolean {
+        return Utils.useQsMediaPlayer(context)
+    }
+
     fun getNotificationBuckets(): IntArray {
         return when {
-            isFilteringEnabled() ->
+            isFilteringEnabled() && isMediaControlsEnabled() ->
+                intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING,
+                    BUCKET_SILENT)
+            !isFilteringEnabled() && isMediaControlsEnabled() ->
+                intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT)
+            isFilteringEnabled() && !isMediaControlsEnabled() ->
                 intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
             NotificationUtils.useNewInterruptionModel(context) ->
                 intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index 0437877..cf670bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -142,9 +142,11 @@
         // To attach rows we can use _this one weird trick_: if the intended view to add does not
         // have a parent, then simply add it (and its children).
         entries.forEach { entry ->
-            val listItem = rowRegistry.requireView(entry)
+            // TODO: We should eventually map GroupEntry's themselves to views so that we don't
+            // depend on representativeEntry here which may actually be null in the future
+            val listItem = rowRegistry.requireView(entry.representativeEntry!!)
 
-            if (listItem.view.parent != null) {
+            if (listItem.view.parent == null) {
                 listContainer.addListItem(listItem)
                 stabilityManager.notifyViewAddition(listItem.view)
             }
@@ -153,7 +155,8 @@
                 for ((idx, childEntry) in entry.children.withIndex()) {
                     val childListItem = rowRegistry.requireView(childEntry)
                     // Child hasn't been added yet. add it!
-                    if (!listItem.notificationChildren.contains(childListItem)) {
+                    if (listItem.notificationChildren == null ||
+                            !listItem.notificationChildren.contains(childListItem)) {
                         // TODO: old code here just Log.wtf()'d here. This might wreak havoc
                         if (childListItem.view.parent != null) {
                             throw IllegalStateException("trying to add a notification child that " +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7019b5b..808e1b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -108,6 +108,12 @@
     /** If this notification was filtered out, then the filter that did the filtering. */
     @Nullable NotifFilter mExcludingFilter;
 
+    /**
+     * The NotifFilter, if any, that was active on this notification during the previous run of
+     * the list builder.
+     */
+    @Nullable NotifFilter mPreviousExcludingFilter;
+
     /** If this was a group child that was promoted to the top level, then who did the promoting. */
     @Nullable NotifPromoter mNotifPromoter;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index f7d6cef..19f7cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -316,6 +316,7 @@
 
         // Step 7: Lock in our group structure and log anything that's changed since the last run
         mPipelineState.incrementTo(STATE_FINALIZING);
+        logFilterChanges();
         logParentingChanges();
         freeEmptyGroups();
 
@@ -363,6 +364,9 @@
             entry.setPreviousParent(entry.getParent());
             entry.setParent(null);
 
+            entry.mPreviousExcludingFilter = entry.mExcludingFilter;
+            entry.mExcludingFilter = null;
+
             if (entry.mFirstAddedIteration == -1) {
                 entry.mFirstAddedIteration = mIterationCount;
             }
@@ -371,8 +375,10 @@
         mNotifList.clear();
     }
 
-    private void filterNotifs(Collection<? extends ListEntry> entries,
-            List<ListEntry> out, List<NotifFilter> filters) {
+    private void filterNotifs(
+            Collection<? extends ListEntry> entries,
+            List<ListEntry> out,
+            List<NotifFilter> filters) {
         final long now = mSystemClock.uptimeMillis();
         for (ListEntry entry : entries)  {
             if (entry instanceof GroupEntry) {
@@ -585,8 +591,9 @@
      * filtered out during any of the filtering steps.
      */
     private void annulAddition(ListEntry entry) {
-        entry.setSection(-1);
-        entry.mNotifSection = null;
+        // TODO: We should null out the entry's section and promoter here. However, if we do that,
+        //  future runs will think that the section changed. We need a mPreviousNotifSection,
+        //  similar to what we do for parents.
         entry.setParent(null);
         if (entry.mFirstAddedIteration == mIterationCount) {
             entry.mFirstAddedIteration = -1;
@@ -615,6 +622,17 @@
         mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
     }
 
+    private void logFilterChanges() {
+        for (NotificationEntry entry : mAllEntries) {
+            if (entry.mExcludingFilter != entry.mPreviousExcludingFilter) {
+                mLogger.logFilterChanged(
+                        entry.getKey(),
+                        entry.mPreviousExcludingFilter,
+                        entry.mExcludingFilter);
+            }
+        }
+    }
+
     private void logParentingChanges() {
         for (NotificationEntry entry : mAllEntries) {
             if (entry.getParent() != entry.getPreviousParent()) {
@@ -680,21 +698,8 @@
     };
 
     private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
-        NotifFilter filter = findRejectingFilter(entry, now, filters);
-
-        if (filter != entry.mExcludingFilter) {
-            mLogger.logFilterChanged(
-                    entry.getKey(),
-                    entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null,
-                    filter != null ? filter.getName() : null);
-
-            // Note that groups and summaries can also be filtered out later if they're part of a
-            // malformed group. We currently don't have a great way to track that beyond parenting
-            // change logs. Consider adding something similar to mExcludingFilter for them.
-            entry.mExcludingFilter = filter;
-        }
-
-        return filter != null;
+        entry.mExcludingFilter = findRejectingFilter(entry, now, filters);
+        return entry.mExcludingFilter != null;
     }
 
     @Nullable private static NotifFilter findRejectingFilter(NotificationEntry entry, long now,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 7e9e760..e9cbf32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -45,7 +45,8 @@
     public void attach(NotifPipeline pipeline) {
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
 
-        pipeline.addPreGroupFilter(mNotifFilter);
+        pipeline.addPreGroupFilter(mSuspendedFilter);
+        pipeline.addPreGroupFilter(mDozingFilter);
     }
 
     /**
@@ -53,33 +54,30 @@
      * NotifListBuilder invalidates the notification list each time the ranking is updated,
      * so we don't need to explicitly invalidate this filter on ranking update.
      */
-    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
-            // App suspended from Ranking
-            if (entry.getRanking().isSuspended()) {
-                return true;
-            }
+            return entry.getRanking().isSuspended();
+        }
+    };
 
+    private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
             // Dozing + DND Settings from Ranking object
             if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
                 return true;
             }
 
-            if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
-                return true;
-            }
-
-            return false;
+            return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList();
         }
     };
 
-
     private final StatusBarStateController.StateListener mStatusBarStateCallback =
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onDozingChanged(boolean isDozing) {
-                    mNotifFilter.invalidateList();
+                    mDozingFilter.invalidateList();
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 763547c..e946cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.log.dagger.NotificationLog
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import javax.inject.Inject
 
 class ShadeListBuilderLogger @Inject constructor(
@@ -126,13 +127,13 @@
 
     fun logFilterChanged(
         key: String,
-        prevFilter: String?,
-        newFilter: String?
+        prevFilter: NotifFilter?,
+        newFilter: NotifFilter?
     ) {
         buffer.log(TAG, INFO, {
             str1 = key
-            str2 = prevFilter
-            str3 = newFilter
+            str2 = prevFilter?.name
+            str3 = newFilter?.name
         }, {
             "Filter changed for $str1: $str2 -> $str3"
         })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
new file mode 100644
index 0000000..ab055e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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.systemui.statusbar.notification.stack;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+
+/**
+ * Root view to insert Lock screen media controls into the notification stack.
+ */
+public class MediaHeaderView extends ActivatableNotificationView {
+
+    private View mContentView;
+
+    public MediaHeaderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContentView = findViewById(R.id.keyguard_media_view);
+    }
+
+    @Override
+    protected View getContentView() {
+        return mContentView;
+    }
+
+    /**
+     * Sets the background color, to be used when album art changes.
+     * @param color background
+     */
+    public void setBackgroundColor(int color) {
+        setTintColor(color);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index f6f8363..5fd11fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -30,6 +30,7 @@
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardMediaPlayer;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,6 +72,7 @@
     private final StatusBarStateController mStatusBarStateController;
     private final ConfigurationController mConfigurationController;
     private final PeopleHubViewAdapter mPeopleHubViewAdapter;
+    private final KeyguardMediaPlayer mKeyguardMediaPlayer;
     private final NotificationSectionsFeatureManager mSectionsFeatureManager;
     private final int mNumberOfSections;
 
@@ -110,17 +112,21 @@
     private boolean mPeopleHubVisible = false;
     @Nullable private Subscription mPeopleHubSubscription;
 
+    private MediaHeaderView mMediaControlsView;
+
     @Inject
     NotificationSectionsManager(
             ActivityStarter activityStarter,
             StatusBarStateController statusBarStateController,
             ConfigurationController configurationController,
             PeopleHubViewAdapter peopleHubViewAdapter,
+            KeyguardMediaPlayer keyguardMediaPlayer,
             NotificationSectionsFeatureManager sectionsFeatureManager) {
         mActivityStarter = activityStarter;
         mStatusBarStateController = statusBarStateController;
         mConfigurationController = configurationController;
         mPeopleHubViewAdapter = peopleHubViewAdapter;
+        mKeyguardMediaPlayer = keyguardMediaPlayer;
         mSectionsFeatureManager = sectionsFeatureManager;
         mNumberOfSections = mSectionsFeatureManager.getNumberOfBuckets();
     }
@@ -188,6 +194,13 @@
         }
         mPeopleHubView = reinflateView(mPeopleHubView, layoutInflater, R.layout.people_strip);
         mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
+
+        if (mMediaControlsView != null) {
+            mKeyguardMediaPlayer.unbindView();
+        }
+        mMediaControlsView = reinflateView(mMediaControlsView, layoutInflater,
+                R.layout.keyguard_media_header);
+        mKeyguardMediaPlayer.bindView(mMediaControlsView);
     }
 
     /** Listener for when the "clear all" button is clicked on the gentle notification header. */
@@ -198,6 +211,7 @@
     @Override
     public boolean beginsSection(@NonNull View view, @Nullable View previous) {
         return view == mGentleHeader
+                || view == mMediaControlsView
                 || view == mPeopleHubView
                 || view == mAlertingHeader
                 || !Objects.equals(getBucket(view), getBucket(previous));
@@ -211,6 +225,8 @@
     private Integer getBucket(View view) {
         if (view == mGentleHeader) {
             return BUCKET_SILENT;
+        } else if (view == mMediaControlsView) {
+            return BUCKET_MEDIA_CONTROLS;
         } else if (view == mPeopleHubView) {
             return BUCKET_PEOPLE;
         } else if (view == mAlertingHeader) {
@@ -238,9 +254,15 @@
 
         final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
         final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled();
+        final boolean isKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+        final boolean usingMediaControls = mSectionsFeatureManager.isMediaControlsEnabled();
 
         boolean peopleNotifsPresent = false;
 
+        int currentMediaControlsIdx = -1;
+        // Currently, just putting media controls in the front and incrementing the position based
+        // on the number of heads-up notifs.
+        int mediaControlsTarget = isKeyguard && usingMediaControls ? 0 : -1;
         int currentPeopleHeaderIdx = -1;
         int peopleHeaderTarget = -1;
         int currentAlertingHeaderIdx = -1;
@@ -255,6 +277,10 @@
             View child = mParent.getChildAt(i);
 
             // Track the existing positions of the headers
+            if (child == mMediaControlsView) {
+                currentMediaControlsIdx = i;
+                continue;
+            }
             if (child == mPeopleHubView) {
                 currentPeopleHeaderIdx = i;
                 continue;
@@ -276,6 +302,9 @@
             // Once we enter a new section, calculate the target position for the header.
             switch (row.getEntry().getBucket()) {
                 case BUCKET_HEADS_UP:
+                    if (mediaControlsTarget != -1) {
+                        mediaControlsTarget++;
+                    }
                     break;
                 case BUCKET_PEOPLE:
                     peopleNotifsPresent = true;
@@ -345,6 +374,8 @@
                 alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
         adjustHeaderVisibilityAndPosition(
                 peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
+        adjustViewPosition(
+                mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
 
         // Update headers to reflect state of section contents
         mGentleHeader.setAreThereDismissableGentleNotifs(
@@ -378,6 +409,28 @@
         }
     }
 
+    private void adjustViewPosition(int targetPosition, ExpandableView header,
+            int currentPosition) {
+        if (targetPosition == -1) {
+            if (currentPosition != -1) {
+                mParent.removeView(header);
+            }
+        } else {
+            if (currentPosition == -1) {
+                // If the header is animating away, it will still have a parent, so detach it first
+                // TODO: We should really cancel the active animations here. This will happen
+                // automatically when the view's intro animation starts, but it's a fragile link.
+                if (header.getTransientContainer() != null) {
+                    header.getTransientContainer().removeTransientView(header);
+                    header.setTransientContainer(null);
+                }
+                mParent.addView(header, targetPosition);
+            } else {
+                mParent.changeViewPosition(header, targetPosition);
+            }
+        }
+    }
+
     /**
      * Updates the boundaries (as tracked by their first and last views) of the priority sections.
      *
@@ -463,6 +516,11 @@
     }
 
     @VisibleForTesting
+    ExpandableView getMediaControlsView() {
+        return mMediaControlsView;
+    }
+
+    @VisibleForTesting
     void setPeopleHubVisible(boolean visible) {
         mPeopleHubVisible = visible;
     }
@@ -501,13 +559,15 @@
     @Retention(SOURCE)
     @IntDef(prefix = { "BUCKET_" }, value = {
             BUCKET_HEADS_UP,
+            BUCKET_MEDIA_CONTROLS,
             BUCKET_PEOPLE,
             BUCKET_ALERTING,
             BUCKET_SILENT
     })
     public @interface PriorityBucket {}
     public static final int BUCKET_HEADS_UP = 0;
-    public static final int BUCKET_PEOPLE = 1;
-    public static final int BUCKET_ALERTING = 2;
-    public static final int BUCKET_SILENT = 3;
+    public static final int BUCKET_MEDIA_CONTROLS = 1;
+    public static final int BUCKET_PEOPLE = 2;
+    public static final int BUCKET_ALERTING = 3;
+    public static final int BUCKET_SILENT = 4;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4d4a2ded..823b186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -222,6 +222,7 @@
     private boolean mIsScrollerBoundSet;
     private Runnable mFinishScrollingCallback;
     private int mTouchSlop;
+    private float mSlopMultiplier;
     private int mMinimumVelocity;
     private int mMaximumVelocity;
     private int mOverflingDistance;
@@ -1022,6 +1023,7 @@
         setClipChildren(false);
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
+        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mOverflingDistance = configuration.getScaledOverflingDistance();
@@ -3744,15 +3746,23 @@
         mLongPressListener = listener;
     }
 
+    private float getTouchSlop(MotionEvent event) {
+        // Adjust the touch slop if another gesture may be being performed.
+        return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                ? mTouchSlop * mSlopMultiplier
+                : mTouchSlop;
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onTouchEvent(MotionEvent ev) {
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
                 || ev.getActionMasked() == MotionEvent.ACTION_UP;
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
         boolean swipingInProgress = mSwipingInProgress;
-        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
             if (isCancelOrUp) {
                 mExpandHelper.onlyObserveMovements(false);
             }
@@ -3778,7 +3788,6 @@
         }
 
         // Check if we need to clear any snooze leavebehinds
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
                 && guts.getGutsContent() instanceof NotificationSnooze) {
             NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
@@ -3891,12 +3900,13 @@
                 int deltaY = mLastMotionY - y;
                 final int xDiff = Math.abs(x - mDownX);
                 final int yDiff = Math.abs(deltaY);
-                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+                final float touchSlop = getTouchSlop(ev);
+                if (!mIsBeingDragged && yDiff > touchSlop && yDiff > xDiff) {
                     setIsBeingDragged(true);
                     if (deltaY > 0) {
-                        deltaY -= mTouchSlop;
+                        deltaY -= touchSlop;
                     } else {
-                        deltaY += mTouchSlop;
+                        deltaY += touchSlop;
                     }
                 }
                 if (mIsBeingDragged) {
@@ -4046,9 +4056,11 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         initDownStates(ev);
         handleEmptySpaceClick(ev);
+
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         boolean expandWantsIt = false;
         boolean swipingInProgress = mSwipingInProgress;
-        if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+        if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
             expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
         }
         boolean scrollWantsIt = false;
@@ -4065,7 +4077,6 @@
         }
         // Check if we need to clear any snooze leavebehinds
         boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
                 !expandWantsIt && !scrollWantsIt) {
             mCheckForLeavebehind = false;
@@ -4083,8 +4094,9 @@
     private void handleEmptySpaceClick(MotionEvent ev) {
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_MOVE:
-                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
-                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+                final float touchSlop = getTouchSlop(ev);
+                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > touchSlop
+                        || Math.abs(ev.getX() - mInitialTouchX) > touchSlop)) {
                     mTouchIsClick = false;
                 }
                 break;
@@ -4168,7 +4180,7 @@
                 final int x = (int) ev.getX(pointerIndex);
                 final int yDiff = Math.abs(y - mLastMotionY);
                 final int xDiff = Math.abs(x - mDownX);
-                if (yDiff > mTouchSlop && yDiff > xDiff) {
+                if (yDiff > getTouchSlop(ev) && yDiff > xDiff) {
                     setIsBeingDragged(true);
                     mLastMotionY = y;
                     mDownX = x;
@@ -6466,7 +6478,7 @@
 
     private boolean hasActiveNotifications() {
         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            return mNotifPipeline.getShadeList().isEmpty();
+            return !mNotifPipeline.getShadeList().isEmpty();
         } else {
             return mEntryManager.hasActiveNotifications();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f9726d2..5588c24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1021,7 +1021,8 @@
                     trackMovement(event);
                     return true;
                 }
-                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+                if (Math.abs(h) > getTouchSlop(event)
+                        && Math.abs(h) > Math.abs(x - mInitialTouchX)
                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
                     mQsTracking = true;
                     onQsExpansionStarted();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 2719a32..481401b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -30,8 +30,6 @@
     protected StatusBar mStatusBar;
     protected HeadsUpManagerPhone mHeadsUpManager;
 
-    protected int mTouchSlop;
-
     protected KeyguardBottomAreaView mKeyguardBottomArea;
     private OnConfigurationChangedListener mOnConfigurationChangedListener;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 30367ed..83cc4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -91,7 +91,8 @@
     protected boolean mTracking;
     private boolean mTouchSlopExceeded;
     private int mTrackingPointer;
-    protected int mTouchSlop;
+    private int mTouchSlop;
+    private float mSlopMultiplier;
     protected boolean mHintAnimationRunning;
     private boolean mOverExpandedBeforeFling;
     private boolean mTouchAboveFalsingThreshold;
@@ -260,11 +261,19 @@
     protected void loadDimens() {
         final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
+        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
         mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
         mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
                 R.dimen.unlock_falsing_threshold);
     }
 
+    protected float getTouchSlop(MotionEvent event) {
+        // Adjust the touch slop if another gesture may be being performed.
+        return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                ? mTouchSlop * mSlopMultiplier
+                : mTouchSlop;
+    }
+
     private void addMovement(MotionEvent event) {
         // Add movement to velocity tracker using raw screen X and Y coordinates instead
         // of window coordinates because the window frame may be moving at the same time.
@@ -1111,7 +1120,8 @@
                     addMovement(event);
                     if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
                         float hAbs = Math.abs(h);
-                        if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
+                        float touchSlop = getTouchSlop(event);
+                        if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
                                 && hAbs > Math.abs(x - mInitialTouchX)) {
                             cancelHeightAnimator();
                             startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
@@ -1228,7 +1238,8 @@
 
                     // If the panel was collapsed when touching, we only need to check for the
                     // y-component of the gesture, as we have no conflicting horizontal gesture.
-                    if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX)
+                    if (Math.abs(h) > getTouchSlop(event)
+                            && (Math.abs(h) > Math.abs(x - mInitialTouchX)
                             || mIgnoreXTouchSlop)) {
                         mTouchSlopExceeded = true;
                         if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1c1e7c4..977a307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -66,7 +66,7 @@
         }
     };
     private DarkReceiver mBattery;
-    private int mRotationOrientation;
+    private int mRotationOrientation = -1;
     @Nullable
     private View mCenterIconSpace;
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 760a6d6..c6f7983 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -99,11 +99,12 @@
         }
 
         // padding needed for corner cutout.
-        int leftCornerCutoutPadding = 0;
-        int rightCornerCutoutPadding = 0;
+        int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
+        int rightCornerCutoutPadding = cutout.getSafeInsetRight();
         if (cornerCutoutPadding != null) {
-            leftCornerCutoutPadding = cornerCutoutPadding.first;
-            rightCornerCutoutPadding = cornerCutoutPadding.second;
+            leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
+            rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
+                    cornerCutoutPadding.second);
         }
 
         return new Pair<>(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
new file mode 100644
index 0000000..464a740
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.keyguard
+
+import android.graphics.drawable.Icon
+import android.media.MediaMetadata
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+public class KeyguardMediaPlayerTest : SysuiTestCase() {
+
+    private lateinit var keyguardMediaPlayer: KeyguardMediaPlayer
+    private lateinit var fakeExecutor: FakeExecutor
+    private lateinit var mediaMetadata: MediaMetadata.Builder
+    private lateinit var entry: NotificationEntryBuilder
+    @Mock private lateinit var mockView: View
+    private lateinit var textView: TextView
+    @Mock private lateinit var mockIcon: Icon
+
+    @Before
+    public fun setup() {
+        fakeExecutor = FakeExecutor(FakeSystemClock())
+        keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
+        mockView = mock(View::class.java)
+        textView = TextView(context)
+        mockIcon = mock(Icon::class.java)
+        mediaMetadata = MediaMetadata.Builder()
+        entry = NotificationEntryBuilder()
+
+        keyguardMediaPlayer.bindView(mockView)
+    }
+
+    @After
+    public fun tearDown() {
+        keyguardMediaPlayer.unbindView()
+    }
+
+    @Test
+    public fun testBind() {
+        keyguardMediaPlayer.unbindView()
+        keyguardMediaPlayer.bindView(mockView)
+    }
+
+    @Test
+    public fun testUnboundClearControls() {
+        keyguardMediaPlayer.unbindView()
+        keyguardMediaPlayer.clearControls()
+        keyguardMediaPlayer.bindView(mockView)
+    }
+
+    @Test
+    public fun testUpdateControls() {
+        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        verify(mockView).setVisibility(View.VISIBLE)
+    }
+
+    @Test
+    public fun testClearControls() {
+        keyguardMediaPlayer.clearControls()
+        verify(mockView).setVisibility(View.GONE)
+    }
+
+    @Test
+    public fun testSongName() {
+        whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(textView)
+        val song: String = "Song"
+        mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
+
+        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+
+        assertThat(textView.getText()).isEqualTo(song)
+    }
+
+    @Test
+    public fun testArtistName() {
+        whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(textView)
+        val artist: String = "Artist"
+        mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
+
+        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+
+        assertThat(textView.getText()).isEqualTo(artist)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index 731101c..afcd441 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -40,14 +41,13 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.phone.NavigationModeController;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -73,15 +73,16 @@
     @Mock private AssistHandleBehaviorController.BehaviorController mMockTestBehavior;
     @Mock private NavigationModeController mMockNavigationModeController;
     @Mock private AssistHandleViewController mMockAssistHandleViewController;
+    @Mock private AccessibilityManager mMockA11yManager;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mDependency.injectMockDependency(StatusBarStateController.class);
-        mDependency.injectMockDependency(OverviewProxyService.class);
         doAnswer(answerVoid(Runnable::run)).when(mMockHandler).post(any(Runnable.class));
         doAnswer(answerVoid(Runnable::run)).when(mMockHandler)
                 .postDelayed(any(Runnable.class), anyLong());
+        doAnswer(invocation -> invocation.getArgument(0)).when(mMockA11yManager)
+                .getRecommendedTimeoutMillis(anyInt(), anyInt());
 
         Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController> behaviorMap =
                 new EnumMap<>(AssistHandleBehavior.class);
@@ -99,6 +100,7 @@
                         mMockDeviceConfigHelper,
                         behaviorMap,
                         mMockNavigationModeController,
+                        () -> mMockA11yManager,
                         mock(DumpManager.class));
     }
 
@@ -243,6 +245,28 @@
     }
 
     @Test
+    public void showAndGo_usesA11yTimeout() {
+        // Arrange
+        when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
+        when(mMockDeviceConfigHelper.getLong(
+                eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS), anyLong()))
+                .thenReturn(12345L);
+        mAssistHandleBehaviorController.hide();
+        reset(mMockAssistHandleViewController, mMockA11yManager);
+        when(mMockA11yManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn(54321);
+        ArgumentCaptor<Long> delay = ArgumentCaptor.forClass(Long.class);
+
+        // Act
+        mAssistHandleBehaviorController.showAndGo();
+
+        // Assert
+        verify(mMockA11yManager).getRecommendedTimeoutMillis(
+                eq(12345), eq(AccessibilityManager.FLAG_CONTENT_ICONS));
+        verify(mMockHandler).postDelayed(any(Runnable.class), delay.capture());
+        assert delay.getValue() == 54321L;
+    }
+
+    @Test
     public void showAndGoDelayed_showsThenHidesHandlesWhenHiding() {
         // Arrange
         when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 9adab5d..5e0d28f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.controller.ControlInfo
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -156,12 +157,26 @@
     }
 
     @Test
+    fun testAddFavorite_changesModelFlag() {
+        val indexToAdd = 6
+        val id = "$idPrefix$indexToAdd"
+        model.changeFavoriteStatus(id, true)
+        assertTrue(
+                (model.elements.first {
+                    it is ControlWrapper && it.controlStatus.control.controlId == id
+                } as ControlWrapper)
+                        .controlStatus.favorite
+        )
+    }
+
+    @Test
     fun testAddFavorite_alreadyThere() {
         val indexToAdd = 7
         model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
 
         val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
 
+        assertEquals(expectedFavorites.size, model.favorites.size)
         model.favorites.zip(expectedFavorites).forEach {
             assertTrue(sameControl(it.first, it.second))
         }
@@ -182,6 +197,19 @@
     }
 
     @Test
+    fun testRemoveFavorite_changesModelFlag() {
+        val indexToRemove = 3
+        val id = "$idPrefix$indexToRemove"
+        model.changeFavoriteStatus(id, false)
+        assertFalse(
+                (model.elements.first {
+                    it is ControlWrapper && it.controlStatus.control.controlId == id
+                } as ControlWrapper)
+                        .controlStatus.favorite
+        )
+    }
+
+    @Test
     fun testRemoveFavorite_notThere() {
         val indexToRemove = 4
         model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index f061f34..f4583f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -22,7 +22,6 @@
 import android.view.Choreographer
 import android.view.View
 import android.view.ViewRootImpl
-import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -35,10 +34,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
 @RunWith(AndroidTestingRunner::class)
@@ -56,7 +59,8 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var root: View
     @Mock private lateinit var viewRootImpl: ViewRootImpl
-    @Mock private lateinit var shadeSpring: SpringAnimation
+    @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
+    @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
     private lateinit var statusBarStateListener: StatusBarStateController.StateListener
@@ -76,6 +80,7 @@
                 keyguardStateController, choreographer, wallpaperManager,
                 notificationShadeWindowController, dumpManager)
         notificationShadeDepthController.shadeSpring = shadeSpring
+        notificationShadeDepthController.globalActionsSpring = globalActionsSpring
         notificationShadeDepthController.root = root
 
         val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
@@ -92,7 +97,7 @@
     fun onPanelExpansionChanged_apliesBlur_ifShade() {
         notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                 false /* tracking */)
-        verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+        verify(shadeSpring).animateTo(eq(maxBlur), any())
     }
 
     @Test
@@ -102,13 +107,13 @@
 
         statusBarState = StatusBarState.KEYGUARD
         statusBarStateListener.onStateChanged(statusBarState)
-        verify(shadeSpring).animateToFinalPosition(eq(0f))
+        verify(shadeSpring).animateTo(eq(0), any())
     }
 
     @Test
-    fun updateGlobalDialogVisibility_schedulesUpdate() {
+    fun updateGlobalDialogVisibility_appliesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
-        verify(choreographer).postFrameCallback(any())
+        verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root))
     }
 
     private fun <T : Any> safeEq(value: T): T {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 6388fe1..b501a2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.DeviceConfigProxyFake
 
+import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -38,6 +39,7 @@
 class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
     var manager: NotificationSectionsFeatureManager? = null
     val proxyFake = DeviceConfigProxyFake()
+    var originalQsMediaPlayer: Int = 0
 
     @Before
     public fun setup() {
@@ -45,6 +47,15 @@
         NOTIFICATION_NEW_INTERRUPTION_MODEL, 1)
         manager = NotificationSectionsFeatureManager(proxyFake, mContext)
         manager!!.clearCache()
+        originalQsMediaPlayer = Settings.System.getInt(context.getContentResolver(),
+                "qs_media_player", 1)
+        Settings.System.putInt(context.getContentResolver(), "qs_media_player", 0)
+    }
+
+    @After
+    public fun teardown() {
+        Settings.System.putInt(context.getContentResolver(), "qs_media_player",
+                originalQsMediaPlayer)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 261dc82..f4fbd7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -48,7 +48,7 @@
 
     /* ListEntry properties */
     private GroupEntry mParent;
-    private int mSection;
+    private int mSection = -1;
 
     public NotificationEntry build() {
         StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index e84f9cf..85acbe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,9 +19,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,6 +41,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -51,20 +51,23 @@
 
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private NotifPipeline mNotifPipeline;
+
+    @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
     private NotificationEntry mEntry;
-    private RankingCoordinator mRankingCoordinator;
-    private NotifFilter mRankingFilter;
+    private NotifFilter mCapturedSuspendedFilter;
+    private NotifFilter mCapturedDozingFilter;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mRankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+        RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
         mEntry = new NotificationEntryBuilder().build();
 
-        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
-        mRankingCoordinator.attach(mNotifPipeline);
-        verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
-        mRankingFilter = filterCaptor.getValue();
+        rankingCoordinator.attach(mNotifPipeline);
+        verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
+        mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
+        mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
     }
 
     @Test
@@ -73,7 +76,7 @@
         mEntry.setRanking(getRankingForUnfilteredNotif().build());
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -84,7 +87,7 @@
                 .build());
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -98,13 +101,13 @@
         when(mStatusBarStateController.isDozing()).thenReturn(true);
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
 
         // WHEN it's not dozing (showing the notification list)
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -118,13 +121,13 @@
         when(mStatusBarStateController.isDozing()).thenReturn(true);
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
 
         // WHEN it's not dozing (showing the notification list)
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
     }
 
     private RankingBuilder getRankingForUnfilteredNotif() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index a263a72..646bc96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -42,6 +42,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardMediaPlayer;
 import com.android.systemui.ActivityStarterDelegate;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -73,6 +74,7 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private ConfigurationController mConfigurationController;
     @Mock private PeopleHubViewAdapter mPeopleHubAdapter;
+    @Mock private KeyguardMediaPlayer mKeyguardMediaPlayer;
     @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
     @Mock private NotificationRowComponent mNotificationRowComponent;
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -91,6 +93,7 @@
                         mStatusBarStateController,
                         mConfigurationController,
                         mPeopleHubAdapter,
+                        mKeyguardMediaPlayer,
                         mSectionsFeatureManager
                 );
         // Required in order for the header inflation to work properly
@@ -333,13 +336,82 @@
         verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
     }
 
+    @Test
+    public void testMediaControls_AddWhenEnterKeyguard() {
+        enableMediaControls();
+
+        // GIVEN a stack that doesn't include media controls
+        setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE);
+
+        // WHEN we go back to the keyguard
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the media controls are added
+        verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 0);
+    }
+
+    @Test
+    public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
+        enableMediaControls();
+
+        // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+        setStackState(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+                ChildType.GENTLE);
+
+        // WHEN we go back to the keyguard
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the media controls are added after HEADS_UP
+        verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 1);
+    }
+
+    @Test
+    public void testMediaControls_RemoveWhenExitKeyguard() {
+        enableMediaControls();
+
+        // GIVEN a stack with media controls
+        setStackState(ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+                ChildType.GENTLE);
+
+        // WHEN we leave the keyguard
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the media controls is removed
+        verify(mNssl).removeView(mSectionsManager.getMediaControlsView());
+    }
+
+    @Test
+    public void testMediaControls_RemoveWhenPullDownShade() {
+        enableMediaControls();
+
+        // GIVEN a stack with media controls
+        setStackState(ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+                ChildType.GENTLE);
+
+        // WHEN we pull down the shade on the keyguard
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the media controls is removed
+        verify(mNssl).removeView(mSectionsManager.getMediaControlsView());
+    }
+
     private void enablePeopleFiltering() {
         when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
         when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
     }
 
+    private void enableMediaControls() {
+        when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true);
+        when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
+    }
+
     private enum ChildType {
-        PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER
+        MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING,
+            GENTLE, OTHER
     }
 
     private void setStackState(ChildType... children) {
@@ -347,6 +419,9 @@
         for (int i = 0; i < children.length; i++) {
             View child;
             switch (children[i]) {
+                case MEDIA_CONTROLS:
+                    child = mSectionsManager.getMediaControlsView();
+                    break;
                 case PEOPLE_HEADER:
                     child = mSectionsManager.getPeopleHeaderView();
                     break;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e49c1ed..c6a54fc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -50,6 +50,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@
     private final LocalLog mWtfHistory = new LocalLog(50);
 
     private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+    private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();
 
     private final LocalService mLocalService = new LocalService();
     private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@
     @Override // from AbstractMasterSystemService
     protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
-        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
-                mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
+        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
+                resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
     }
 
     @Override // AbstractMasterSystemService
     protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
             @UserIdInt int userId) {
         service.destroyLocked();
+        mDisabledInfoCache.remove(userId);
         mAutofillCompatState.removeCompatibilityModeRequests(userId);
     }
 
@@ -835,15 +839,10 @@
 
         private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
                 String packageName) {
-            synchronized (mLock) {
-                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
-                if (service != null) {
-                    options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
-                            packageName);
-                    options.disabledActivities = service.getAppDisabledActivitiesLocked(
-                            packageName);
-                }
-            }
+            options.appDisabledExpiration =
+                    mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
+            options.disabledActivities =
+                    mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
         }
     }
 
@@ -867,6 +866,234 @@
     }
 
     /**
+     * Stores autofill disable information, i.e. {@link AutofillDisabledInfo},  keyed by user id.
+     * The information is cleaned up when the service is removed.
+     */
+    static final class DisabledInfoCache {
+
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();
+
+        void remove(@UserIdInt int userId) {
+            synchronized (mLock) {
+                mCache.remove(userId);
+            }
+        }
+
+        void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
+                long expiration) {
+            Preconditions.checkNotNull(packageName);
+            synchronized (mLock) {
+                AutofillDisabledInfo info =
+                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+                info.putDisableAppsLocked(packageName, expiration);
+            }
+        }
+
+        void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
+                long expiration) {
+            Preconditions.checkNotNull(componentName);
+            synchronized (mLock) {
+                AutofillDisabledInfo info =
+                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+                info.putDisableActivityLocked(componentName, expiration);
+            }
+        }
+
+        boolean isAutofillDisabledLocked(@UserIdInt int userId,
+                @NonNull ComponentName componentName) {
+            Preconditions.checkNotNull(componentName);
+            final boolean disabled;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
+            }
+            return disabled;
+        }
+
+        long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
+            Preconditions.checkNotNull(packageName);
+            final Long expiration;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
+            }
+            return expiration;
+        }
+
+        @Nullable
+        ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
+                @NonNull String packageName) {
+            Preconditions.checkNotNull(packageName);
+            final ArrayMap<String, Long> disabledList;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                disabledList =
+                        info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
+            }
+            return disabledList;
+        }
+
+        void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                if (info != null) {
+                    info.dumpLocked(prefix, pw);
+                }
+            }
+        }
+
+        @NonNull
+        private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
+                @UserIdInt int userId) {
+            AutofillDisabledInfo info = mCache.get(userId);
+            if (info == null) {
+                info = new AutofillDisabledInfo();
+                mCache.put(userId, info);
+            }
+            return info;
+        }
+    }
+
+    /**
+     * The autofill disable information.
+     * <p>
+     * This contains disable information set by the AutofillService, e.g. disabled application
+     * expiration, disable activity expiration.
+     */
+    private static final class AutofillDisabledInfo {
+        /**
+         * Apps disabled by the service; key is package name, value is when they will be enabled
+         * again.
+         */
+        private ArrayMap<String, Long> mDisabledApps;
+        /**
+         * Activities disabled by the service; key is component name, value is when they will be
+         * enabled again.
+         */
+        private ArrayMap<ComponentName, Long> mDisabledActivities;
+
+        void putDisableAppsLocked(@NonNull String packageName, long expiration) {
+            if (mDisabledApps == null) {
+                mDisabledApps = new ArrayMap<>(1);
+            }
+            mDisabledApps.put(packageName, expiration);
+        }
+
+        void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
+            if (mDisabledActivities == null) {
+                mDisabledActivities = new ArrayMap<>(1);
+            }
+            mDisabledActivities.put(componentName, expiration);
+        }
+
+        long getAppDisabledExpirationLocked(@NonNull String packageName) {
+            if (mDisabledApps == null) {
+                return 0;
+            }
+            final Long expiration = mDisabledApps.get(packageName);
+            return expiration != null ? expiration : 0;
+        }
+
+        ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+            if (mDisabledActivities != null) {
+                final int size = mDisabledActivities.size();
+                ArrayMap<String, Long> disabledList = null;
+                for (int i = 0; i < size; i++) {
+                    final ComponentName component = mDisabledActivities.keyAt(i);
+                    if (packageName.equals(component.getPackageName())) {
+                        if (disabledList == null) {
+                            disabledList = new ArrayMap<>();
+                        }
+                        final long expiration = mDisabledActivities.valueAt(i);
+                        disabledList.put(component.flattenToShortString(), expiration);
+                    }
+                }
+                return disabledList;
+            }
+            return null;
+        }
+
+        boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+            // Check activities first.
+            long elapsedTime = 0;
+            if (mDisabledActivities != null) {
+                elapsedTime = SystemClock.elapsedRealtime();
+                final Long expiration = mDisabledActivities.get(componentName);
+                if (expiration != null) {
+                    if (expiration >= elapsedTime) return true;
+                    // Restriction expired - clean it up.
+                    if (sVerbose) {
+                        Slog.v(TAG, "Removing " + componentName.toShortString()
+                                + " from disabled list");
+                    }
+                    mDisabledActivities.remove(componentName);
+                }
+            }
+
+            // Then check apps.
+            final String packageName = componentName.getPackageName();
+            if (mDisabledApps == null) return false;
+
+            final Long expiration = mDisabledApps.get(packageName);
+            if (expiration == null) return false;
+
+            if (elapsedTime == 0) {
+                elapsedTime = SystemClock.elapsedRealtime();
+            }
+
+            if (expiration >= elapsedTime) return true;
+
+            // Restriction expired - clean it up.
+            if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
+            mDisabledApps.remove(packageName);
+            return false;
+        }
+
+        void dumpLocked(String prefix, PrintWriter pw) {
+            pw.print(prefix); pw.print("Disabled apps: ");
+            if (mDisabledApps == null) {
+                pw.println("N/A");
+            } else {
+                final int size = mDisabledApps.size();
+                pw.println(size);
+                final StringBuilder builder = new StringBuilder();
+                final long now = SystemClock.elapsedRealtime();
+                for (int i = 0; i < size; i++) {
+                    final String packageName = mDisabledApps.keyAt(i);
+                    final long expiration = mDisabledApps.valueAt(i);
+                    builder.append(prefix).append(prefix)
+                            .append(i).append(". ").append(packageName).append(": ");
+                    TimeUtils.formatDuration((expiration - now), builder);
+                    builder.append('\n');
+                }
+                pw.println(builder);
+            }
+
+            pw.print(prefix); pw.print("Disabled activities: ");
+            if (mDisabledActivities == null) {
+                pw.println("N/A");
+            } else {
+                final int size = mDisabledActivities.size();
+                pw.println(size);
+                final StringBuilder builder = new StringBuilder();
+                final long now = SystemClock.elapsedRealtime();
+                for (int i = 0; i < size; i++) {
+                    final ComponentName component = mDisabledActivities.keyAt(i);
+                    final long expiration = mDisabledActivities.valueAt(i);
+                    builder.append(prefix).append(prefix)
+                            .append(i).append(". ").append(component).append(": ");
+                    TimeUtils.formatDuration((expiration - now), builder);
+                    builder.append('\n');
+                }
+                pw.println(builder);
+            }
+        }
+    }
+
+    /**
      * Compatibility mode metadata associated with all services.
      *
      * <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6fbe141..d1805d9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -67,7 +67,6 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
 import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
-
 /**
  * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
  * app's {@link IAutoFillService} implementation.
@@ -125,19 +124,6 @@
     private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
 
     /**
-     * Apps disabled by the service; key is package name, value is when they will be enabled again.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<String, Long> mDisabledApps;
-
-    /**
-     * Activities disabled by the service; key is component name, value is when they will be enabled
-     * again.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<ComponentName, Long> mDisabledActivities;
-
-    /**
      * Data used for field classification.
      */
     @GuardedBy("mLock")
@@ -186,10 +172,12 @@
 
     private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
 
+    private final DisabledInfoCache mDisabledInfoCache;
+
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState,
-            boolean disabled) {
+            boolean disabled, DisabledInfoCache disableCache) {
         super(master, lock, userId);
 
         mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
         mContentCaptureManagerInternal = LocalServices.getService(
                 ContentCaptureManagerInternal.class);
-
+        mDisabledInfoCache = disableCache;
         updateLocked(disabled);
     }
 
@@ -1045,45 +1033,7 @@
         pw.println(isInlineSuggestionsEnabled());
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
-        pw.print(prefix); pw.print("Disabled apps: ");
-
-        if (mDisabledApps == null) {
-            pw.println("N/A");
-        } else {
-            final int size = mDisabledApps.size();
-            pw.println(size);
-            final StringBuilder builder = new StringBuilder();
-            final long now = SystemClock.elapsedRealtime();
-            for (int i = 0; i < size; i++) {
-                final String packageName = mDisabledApps.keyAt(i);
-                final long expiration = mDisabledApps.valueAt(i);
-                builder.append(prefix).append(prefix)
-                        .append(i).append(". ").append(packageName).append(": ");
-                TimeUtils.formatDuration((expiration - now), builder);
-                builder.append('\n');
-            }
-            pw.println(builder);
-        }
-
-        pw.print(prefix); pw.print("Disabled activities: ");
-
-        if (mDisabledActivities == null) {
-            pw.println("N/A");
-        } else {
-            final int size = mDisabledActivities.size();
-            pw.println(size);
-            final StringBuilder builder = new StringBuilder();
-            final long now = SystemClock.elapsedRealtime();
-            for (int i = 0; i < size; i++) {
-                final ComponentName component = mDisabledActivities.keyAt(i);
-                final long expiration = mDisabledActivities.valueAt(i);
-                builder.append(prefix).append(prefix)
-                        .append(i).append(". ").append(component).append(": ");
-                TimeUtils.formatDuration((expiration - now), builder);
-                builder.append('\n');
-            }
-            pw.println(builder);
-        }
+        mDisabledInfoCache.dump(mUserId, prefix, pw);
 
         final int size = mSessions.size();
         if (size == 0) {
@@ -1480,15 +1430,13 @@
     void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
             boolean compatMode) {
         synchronized (mLock) {
-            if (mDisabledApps == null) {
-                mDisabledApps = new ArrayMap<>(1);
-            }
             long expiration = SystemClock.elapsedRealtime() + duration;
             // Protect it against overflow
             if (expiration < 0) {
                 expiration = Long.MAX_VALUE;
             }
-            mDisabledApps.put(packageName, expiration);
+            mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);
+
             int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
             mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
                     packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@
     void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
             int sessionId, boolean compatMode) {
         synchronized (mLock) {
-            if (mDisabledActivities == null) {
-                mDisabledActivities = new ArrayMap<>(1);
-            }
             long expiration = SystemClock.elapsedRealtime() + duration;
             // Protect it against overflow
             if (expiration < 0) {
                 expiration = Long.MAX_VALUE;
             }
-            mDisabledActivities.put(componentName, expiration);
+            mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
             final int intDuration = duration > Integer.MAX_VALUE
                     ? Integer.MAX_VALUE
                     : (int) duration;
@@ -1528,74 +1473,12 @@
         }
     }
 
-    // Called by AutofillManagerService
-    long getAppDisabledExpirationLocked(@NonNull String packageName) {
-        if (mDisabledApps == null) {
-            return 0;
-        }
-        final Long expiration = mDisabledApps.get(packageName);
-        return expiration != null ? expiration : 0;
-    }
-
-    // Called by AutofillManagerService
-    @Nullable
-    ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
-        if (mDisabledActivities != null) {
-            final int size = mDisabledActivities.size();
-            ArrayMap<String, Long> disabledList = null;
-            for (int i = 0; i < size; i++) {
-                final ComponentName component = mDisabledActivities.keyAt(i);
-                if (packageName.equals(component.getPackageName())) {
-                    if (disabledList == null) {
-                        disabledList = new ArrayMap<>();
-                    }
-                    final long expiration = mDisabledActivities.valueAt(i);
-                    disabledList.put(component.flattenToShortString(), expiration);
-                }
-            }
-            return disabledList;
-        }
-        return null;
-    }
-
     /**
      * Checks if autofill is disabled by service to the given activity.
      */
     @GuardedBy("mLock")
     private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
-        // Check activities first.
-        long elapsedTime = 0;
-        if (mDisabledActivities != null) {
-            elapsedTime = SystemClock.elapsedRealtime();
-            final Long expiration = mDisabledActivities.get(componentName);
-            if (expiration != null) {
-                if (expiration >= elapsedTime) return true;
-                // Restriction expired - clean it up.
-                if (sVerbose) {
-                    Slog.v(TAG, "Removing " + componentName.toShortString()
-                        + " from disabled list");
-                }
-                mDisabledActivities.remove(componentName);
-            }
-        }
-
-        // Then check apps.
-        final String packageName = componentName.getPackageName();
-        if (mDisabledApps == null) return false;
-
-        final Long expiration = mDisabledApps.get(packageName);
-        if (expiration == null) return false;
-
-        if (elapsedTime == 0) {
-            elapsedTime = SystemClock.elapsedRealtime();
-        }
-
-        if (expiration >= elapsedTime) return true;
-
-        // Restriction expired - clean it up.
-        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
-        mDisabledApps.remove(packageName);
-        return false;
+        return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
     }
 
     // Called by AutofillManager, checks UID.
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 5de8171..4ba2c3d 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -199,7 +199,10 @@
             return false;
         }
 
-        if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) {
+        // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
+        //  only compare the view id for now.
+        if (!mImeInputViewStarted || mImeFieldId == null
+                || autofillId.getViewId() != mImeFieldId.getViewId()) {
             if (sDebug) {
                 Log.d(TAG,
                         "onInlineSuggestionsResponseLocked not sent because input view is not "
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index b54ec4e..0fdabd0 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -21,13 +21,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IClassificationsCallback;
 import android.app.contentsuggestions.IContentSuggestionsManager;
 import android.app.contentsuggestions.ISelectionsCallback;
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -133,7 +137,7 @@
                 if (service != null) {
                     // TODO(b/147324195): Temporarily pass bitmap until we change the service API.
                     imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap);
-                    service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras);
+                    service.provideContextImageFromBitmapLocked(imageContextRequestExtras);
                 } else {
                     if (VERBOSE) {
                         Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
@@ -152,10 +156,28 @@
             }
             enforceCaller(UserHandle.getCallingUserId(), "provideContextImage");
 
+            GraphicBuffer snapshotBuffer = null;
+            int colorSpaceId = 0;
+
+            // Skip taking TaskSnapshot when bitmap is provided.
+            if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+                // Can block, so call before acquiring the lock.
+                ActivityManager.TaskSnapshot snapshot =
+                        mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+                if (snapshot != null) {
+                    snapshotBuffer = snapshot.getSnapshot();
+                    ColorSpace colorSpace = snapshot.getColorSpace();
+                    if (colorSpace != null) {
+                        colorSpaceId = colorSpace.getId();
+                    }
+                }
+            }
+
             synchronized (mLock) {
                 final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
                 if (service != null) {
-                    service.provideContextImageLocked(taskId, imageContextRequestExtras);
+                    service.provideContextImageLocked(taskId, snapshotBuffer, colorSpaceId,
+                            imageContextRequestExtras);
                 } else {
                     if (VERBOSE) {
                         Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 7828050..cf53b16 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -19,17 +19,14 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.contentsuggestions.ClassificationsRequest;
-import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IClassificationsCallback;
 import android.app.contentsuggestions.ISelectionsCallback;
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -95,26 +92,17 @@
     }
 
     @GuardedBy("mLock")
-    void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
+    void provideContextImageFromBitmapLocked(@NonNull Bundle bitmapContainingExtras) {
+        // No task or snapshot provided, the bitmap is contained in the extras
+        provideContextImageLocked(-1, null, 0, bitmapContainingExtras);
+    }
+
+    @GuardedBy("mLock")
+    void provideContextImageLocked(int taskId, @Nullable GraphicBuffer snapshot,
+            int colorSpaceIdForSnapshot, @NonNull Bundle imageContextRequestExtras) {
         RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
         if (service != null) {
-            GraphicBuffer snapshotBuffer = null;
-            int colorSpaceId = 0;
-
-            // Skip taking TaskSnapshot when bitmap is provided.
-            if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
-                ActivityManager.TaskSnapshot snapshot =
-                        mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
-                if (snapshot != null) {
-                    snapshotBuffer = snapshot.getSnapshot();
-                    ColorSpace colorSpace = snapshot.getColorSpace();
-                    if (colorSpace != null) {
-                        colorSpaceId = colorSpace.getId();
-                    }
-                }
-            }
-
-            service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+            service.provideContextImage(taskId, snapshot, colorSpaceIdForSnapshot,
                     imageContextRequestExtras);
         }
     }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9b04e79..fe33fae9 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1876,7 +1876,7 @@
                     // package was t(q) then the next delivery must be after t(q) + <window_size>
                     final long t = mAppWakeupHistory.getNthLastWakeupForPackage(
                             sourcePackage, sourceUserId, quotaForBucket);
-                    minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
+                    minElapsed = t + mConstants.APP_STANDBY_WINDOW;
                 }
                 if (alarm.expectedWhenElapsed < minElapsed) {
                     alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4d504e7..ec84ae7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -234,7 +234,6 @@
 import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
 
 /**
  * @hide
@@ -5989,12 +5988,12 @@
      * @return true if routes changed between oldLp and newLp
      */
     private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
-        Function<RouteInfo, IpPrefix> getDestination = (r) -> r.getDestination();
         // compare the route diff to determine which routes have been updated
-        CompareOrUpdateResult<IpPrefix, RouteInfo> routeDiff = new CompareOrUpdateResult<>(
-                oldLp != null ? oldLp.getAllRoutes() : null,
-                newLp != null ? newLp.getAllRoutes() : null,
-                getDestination);
+        final CompareOrUpdateResult<RouteInfo.RouteKey, RouteInfo> routeDiff =
+                new CompareOrUpdateResult<>(
+                        oldLp != null ? oldLp.getAllRoutes() : null,
+                        newLp != null ? newLp.getAllRoutes() : null,
+                        (r) -> r.getRouteKey());
 
         // add routes before removing old in case it helps with continuous connectivity
 
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index cea3251..3148a62 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -103,7 +103,7 @@
     private static boolean PROP_PIN_CAMERA =
             DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
                                     "pin_camera",
-                                    SystemProperties.getBoolean("pinner.pin_camera", true));
+                                    SystemProperties.getBoolean("pinner.pin_camera", false));
     // Pin using pinlist.meta when pinning apps.
     private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
             "pinner.use_pinlist", true);
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 8f914fe..94d6b13 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -25,6 +25,7 @@
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.utils.TimingsTraceAndSlog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -93,6 +94,8 @@
             mPendingTasks.add(description);
         }
         return mService.submit(() -> {
+            TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+            traceLog.traceBegin("InitThreadPoolExec:" + description);
             if (IS_DEBUGGABLE) {
                 Slog.d(TAG, "Started executing " + description);
             }
@@ -100,6 +103,7 @@
                 runnable.run();
             } catch (RuntimeException e) {
                 Slog.e(TAG, "Failure in " + description + ": " + e, e);
+                traceLog.traceEnd();
                 throw e;
             }
             synchronized (mPendingTasks) {
@@ -108,6 +112,7 @@
             if (IS_DEBUGGABLE) {
                 Slog.d(TAG, "Finished executing " + description);
             }
+            traceLog.traceEnd();
         });
     }
 
@@ -132,7 +137,10 @@
      */
     static void shutdown() {
         synchronized (LOCK) {
+            TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+            t.traceBegin("WaitInitThreadPoolShutdown");
             if (sInstance == null) {
+                t.traceEnd();
                 Slog.wtf(TAG, "Already shutdown", new Exception());
                 return;
             }
@@ -147,6 +155,7 @@
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
                 dumpStackTraces();
+                t.traceEnd();
                 throw new IllegalStateException(TAG + " init interrupted");
             }
             if (!terminated) {
@@ -160,11 +169,13 @@
                 synchronized (sInstance.mPendingTasks) {
                     copy.addAll(sInstance.mPendingTasks);
                 }
+                t.traceEnd();
                 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
                         + unstartedRunnables + " Unfinished tasks " + copy);
             }
             sInstance = null; // Make eligible for GC
             Slog.d(TAG, "Shutdown successful");
+            t.traceEnd();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 03f4a54..ba89fce 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,20 +16,15 @@
 
 package com.android.server.am;
 
-import static android.app.AppOpsManager.OP_NONE;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
-import android.app.Activity;
 import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.BugreportManager;
-import android.os.BugreportParams;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -115,17 +110,9 @@
         options.setBackgroundActivityStartsAllowed(true);
         final long identity = Binder.clearCallingIdentity();
         try {
-            // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
-            // let ResultBroadcastReceiver know the handler app is available.
-            context.sendOrderedBroadcastAsUser(intent,
-                    UserHandle.of(handlerUser),
+            context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
                     android.Manifest.permission.DUMP,
-                    OP_NONE, options.toBundle(),
-                    new ResultBroadcastReceiver(),
-                    /* scheduler= */ null,
-                    Activity.RESULT_CANCELED,
-                    /* initialData= */ null,
-                    /* initialExtras= */ null);
+                    options.toBundle());
         } catch (RuntimeException e) {
             Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
             return false;
@@ -189,19 +176,4 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
-
-    private static class ResultBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (getResultCode() == Activity.RESULT_OK) {
-                return;
-            }
-
-            Slog.w(TAG, "Request bug report because handler app seems to be not available.");
-            BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
-            bugreportManager.requestBugreport(
-                    new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
-                    /* shareTitle= */null, /* shareDescription= */ null);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1f0146a..ada3e42 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4570,6 +4570,7 @@
     public void setWiredDeviceConnectionState(int type,
             @ConnectionState int state, String address, String name,
             String caller) {
+        enforceModifyAudioRoutingPermission();
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4b925ef..9bbbc3b 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -155,13 +155,18 @@
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-        final boolean trusted = mContext.checkCallingOrSelfPermission(
+        final boolean hasConfigureWifiDisplayPermission = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
                 == PackageManager.PERMISSION_GRANTED;
+        final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                == PackageManager.PERMISSION_GRANTED;
+
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                registerRouter2Locked(router, uid, pid, packageName, userId, trusted);
+                registerRouter2Locked(router, uid, pid, packageName, userId,
+                        hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -341,8 +346,6 @@
             throw new IllegalArgumentException("packageName must not be empty");
         }
 
-        final boolean trusted = true;
-
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
@@ -350,7 +353,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                registerManagerLocked(manager, uid, pid, packageName, userId, trusted);
+                registerManagerLocked(manager, uid, pid, packageName, userId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -532,7 +535,8 @@
     ////////////////////////////////////////////////////////////////
 
     private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid,
-            @NonNull String packageName, int userId, boolean trusted) {
+            @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission,
+            boolean hasModifyAudioRoutingPermission) {
         final IBinder binder = router.asBinder();
         if (mAllRouterRecords.get(binder) != null) {
             Slog.w(TAG, "Same router already exists. packageName=" + packageName);
@@ -540,8 +544,8 @@
         }
 
         UserRecord userRecord = getOrCreateUserRecordLocked(userId);
-        RouterRecord routerRecord = new RouterRecord(
-                userRecord, router, uid, pid, packageName, trusted);
+        RouterRecord routerRecord = new RouterRecord(userRecord, router, uid, pid, packageName,
+                hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission);
         try {
             binder.linkToDeath(routerRecord, 0);
         } catch (RemoteException ex) {
@@ -711,7 +715,7 @@
     }
 
     private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
-            int uid, int pid, @NonNull String packageName, int userId, boolean trusted) {
+            int uid, int pid, @NonNull String packageName, int userId) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
@@ -721,7 +725,7 @@
         }
 
         UserRecord userRecord = getOrCreateUserRecordLocked(userId);
-        managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted);
+        managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName);
         try {
             binder.linkToDeath(managerRecord, 0);
         } catch (RemoteException ex) {
@@ -778,7 +782,7 @@
             @NonNull IMediaRouter2Manager manager,
             @NonNull String packageName, @NonNull MediaRoute2Info route) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
-        if (managerRecord == null || !managerRecord.mTrusted) {
+        if (managerRecord == null) {
             return;
         }
 
@@ -976,14 +980,16 @@
         public final IMediaRouter2 mRouter;
         public final int mUid;
         public final int mPid;
-        public final boolean mTrusted;
+        public final boolean mHasConfigureWifiDisplayPermission;
+        public final boolean mHasModifyAudioRoutingPermission;
         public final int mRouterId;
 
         public RouteDiscoveryPreference mDiscoveryPreference;
         public MediaRoute2Info mSelectedRoute;
 
-        RouterRecord(UserRecord userRecord, IMediaRouter2 router,
-                int uid, int pid, String packageName, boolean trusted) {
+        RouterRecord(UserRecord userRecord, IMediaRouter2 router, int uid, int pid,
+                String packageName, boolean hasConfigureWifiDisplayPermission,
+                boolean hasModifyAudioRoutingPermission) {
             mUserRecord = userRecord;
             mPackageName = packageName;
             mSelectRouteSequenceNumbers = new ArrayList<>();
@@ -991,7 +997,8 @@
             mRouter = router;
             mUid = uid;
             mPid = pid;
-            mTrusted = trusted;
+            mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission;
+            mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission;
             mRouterId = mNextRouterOrManagerId.getAndIncrement();
         }
 
@@ -1011,17 +1018,15 @@
         public final int mUid;
         public final int mPid;
         public final String mPackageName;
-        public final boolean mTrusted;
         public final int mManagerId;
 
         ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager,
-                int uid, int pid, String packageName, boolean trusted) {
+                int uid, int pid, String packageName) {
             mUserRecord = userRecord;
             mManager = manager;
             mUid = uid;
             mPid = pid;
             mPackageName = packageName;
-            mTrusted = trusted;
             mManagerId = mNextRouterOrManagerId.getAndIncrement();
         }
 
@@ -1036,9 +1041,6 @@
 
         public void dump(PrintWriter pw, String prefix) {
             pw.println(prefix + this);
-
-            final String indent = prefix + "  ";
-            pw.println(indent + "mTrusted=" + mTrusted);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index e5cb554..f2eb176 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -15,9 +15,7 @@
 */
 package com.android.server.notification;
 
-import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_BUBBLE;
-import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
@@ -25,7 +23,6 @@
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -34,8 +31,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 
-import java.util.ArrayList;
-
 /**
  * Determines whether a bubble can be shown for this notification
  */
@@ -152,46 +147,18 @@
                 return false;
             }
 
-            // At this point the bubble must fulfill communication policy
-
-            // Communication always needs a person
-            ArrayList<Person> peopleList = notification.extras != null
-                    ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
-                    : null;
-            // Message style requires a person & it's not included in the list
             boolean isMessageStyle = Notification.MessagingStyle.class.equals(
                     notification.getNotificationStyle());
-            if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
-                logBubbleError(r.getKey(), "Must have a person and be "
-                        + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+            if (!isMessageStyle) {
+                logBubbleError(r.getKey(), "must be Notification.MessageStyle");
                 return false;
             }
-
-            // Communication is a message or a call
-            boolean isCall = CATEGORY_CALL.equals(notification.category);
-            boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
-            if (hasForegroundService && !isCall) {
-                logBubbleError(r.getKey(),
-                        "foreground services must be Notification.CATEGORY_CALL to bubble");
-                return false;
-            }
-            if (isMessageStyle) {
-                return true;
-            } else if (isCall) {
-                if (hasForegroundService) {
-                    return true;
-                }
-                logBubbleError(r.getKey(), "calls require foreground service");
-                return false;
-            }
-            logBubbleError(r.getKey(), "Must be "
-                    + "Notification.MessageStyle or Notification.CATEGORY_CALL");
-            return false;
+            return true;
         }
 
         /**
-         * @return whether the user has enabled the provided notification to bubble, does not
-         * account for policy.
+         * @return whether the user has enabled the provided notification to bubble, and if the
+         * developer has provided valid information for the notification to bubble.
          */
         @VisibleForTesting
         boolean canBubble(NotificationRecord r, String pkg, int userId) {
@@ -217,8 +184,17 @@
             }
 
             String shortcutId = metadata.getShortcutId();
-            boolean shortcutValid = shortcutId != null
-                    && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+            String notificationShortcutId = r.getShortcutInfo() != null
+                    ? r.getShortcutInfo().getId()
+                    : null;
+            boolean shortcutValid = false;
+            if (notificationShortcutId != null && shortcutId != null) {
+                // NoMan already checks validity of shortcut, just check if they match.
+                shortcutValid = shortcutId.equals(notificationShortcutId);
+            } else if (shortcutId != null) {
+                shortcutValid =
+                        mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null;
+            }
             if (metadata.getIntent() == null && !shortcutValid) {
                 // Should have a shortcut if intent is null
                 logBubbleError(r.getKey(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f65f187..7f805be 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -139,6 +139,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -243,7 +244,6 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.TriPredicate;
@@ -388,10 +388,9 @@
      * still post toasts created with
      * {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
      * in the background.
-     *
-     * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
      */
     @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
 
     private IActivityManager mAm;
@@ -2752,24 +2751,18 @@
         @Override
         public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
                 int displayId, @Nullable ITransientNotificationCallback callback) {
-            enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
+            enqueueToast(pkg, token, text, null, duration, displayId, callback);
         }
 
         @Override
         public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
                 int duration, int displayId) {
-            enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
-        }
-
-        @Override
-        public void enqueueTextOrCustomToast(String pkg, IBinder token,
-                ITransientNotification callback, int duration, int displayId, boolean isCustom) {
-            enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
+            enqueueToast(pkg, token, null, callback, duration, displayId, null);
         }
 
         private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
                 @Nullable ITransientNotification callback, int duration, int displayId,
-                @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
+                @Nullable ITransientNotificationCallback textCallback) {
             if (DBG) {
                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
                         + " duration=" + duration + " displayId=" + displayId);
@@ -2808,11 +2801,15 @@
             }
 
             boolean isAppRenderedToast = (callback != null);
-            if (isAppRenderedToast && isCustom && !isSystemToast
-                    && !isPackageInForegroundForToast(pkg, callingUid)) {
+            if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
+                    callingUid)) {
                 boolean block;
                 long id = Binder.clearCallingIdentity();
                 try {
+                    // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
+                    // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
+                    // are not app-rendered, so isAppRenderedToast == true means it's a custom
+                    // toast.
                     block = mPlatformCompat.isChangeEnabledByPackageName(
                             CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg,
                             callingUser.getIdentifier());
@@ -2825,11 +2822,6 @@
                     Binder.restoreCallingIdentity(id);
                 }
                 if (block) {
-                    // TODO(b/144152069): Remove informative toast
-                    mUiHandler.post(() -> Toast.makeText(getContext(),
-                            "Background custom toast blocked for package " + pkg + ".\n"
-                                    + "See g.co/dev/toast.",
-                            Toast.LENGTH_SHORT).show());
                     Slog.w(TAG, "Blocking custom toast from package " + pkg
                             + " due to package not in the foreground");
                     return;
@@ -3454,7 +3446,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(onlyImportant);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         conversation.getPkg(),
                         UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3477,7 +3469,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(pkg, uid);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         pkg,
                         UserHandle.of(UserHandle.getUserId(uid))));
@@ -3639,13 +3631,23 @@
         }
 
         /**
+         * @deprecated Use {@link #getActiveNotificationsWithAttribution(String, String)} instead.
+         */
+        @Deprecated
+        @Override
+        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+            return getActiveNotificationsWithAttribution(callingPkg, null);
+        }
+
+        /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
          * Requires ACCESS_NOTIFICATIONS which is signature|system.
          * @returns A list of all the notifications, in natural order.
          */
         @Override
-        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+        public StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+                String callingAttributionTag) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3655,7 +3657,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 synchronized (mNotificationLock) {
                     tmp = new StatusBarNotification[mNotificationList.size()];
@@ -3737,12 +3740,24 @@
         }
 
         /**
-         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+         * @deprecated Use {@link #getHistoricalNotificationsWithAttribution} instead.
          */
+        @Deprecated
         @Override
         @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
         public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count,
                 boolean includeSnoozed) {
+            return getHistoricalNotificationsWithAttribution(callingPkg, null, count,
+                    includeSnoozed);
+        }
+
+        /**
+         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+         */
+        @Override
+        @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+        public StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+                String callingAttributionTag, int count, boolean includeSnoozed) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3752,7 +3767,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 synchronized (mArchive) {
                     tmp = mArchive.getArray(count, includeSnoozed);
@@ -3768,7 +3784,8 @@
         @Override
         @WorkerThread
         @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
-        public NotificationHistory getNotificationHistory(String callingPkg) {
+        public NotificationHistory getNotificationHistory(String callingPkg,
+                String callingAttributionTag) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3776,7 +3793,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
@@ -5648,7 +5666,8 @@
             }
         }
 
-        r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
+        r.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
+                notification.getShortcutId(), pkg, user));
 
         if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                 r.getSbn().getOverrideGroupKey() != null)) {
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 7bbb3b1..f1ce3a7 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -121,7 +121,10 @@
         mLauncherAppsService = launcherApps;
     }
 
-    ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+    /**
+     * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}.
+     */
+    ShortcutInfo getValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
         if (mLauncherAppsService == null) {
             return null;
         }
@@ -135,20 +138,15 @@
             query.setShortcutIds(Arrays.asList(shortcutId));
             query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
             List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
-            return shortcuts != null && shortcuts.size() > 0
+            ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
                     ? shortcuts.get(0)
                     : null;
+            return info != null && info.isLongLived() ? info : null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    boolean hasValidShortcutInfo(String shortcutId, String packageName,
-            UserHandle user) {
-        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
-        return shortcutInfo != null && shortcutInfo.isLongLived();
-    }
-
     /**
      * Shortcut based bubbles require some extra work to listen for shortcut changes.
      *
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 83da381..7069818 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -249,16 +249,13 @@
     }
 
     private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) {
-        List<UserHandle> targetUserProfiles =
-                getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId());
-        if (targetUserProfiles.isEmpty()) {
+        final int[] enabledProfileIds =
+                mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId());
+        if (enabledProfileIds.length < 2) {
             return false;
         }
-        if (!hasRequestedAppOpPermission(
-                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
-            return false;
-        }
-        return isCrossProfilePackageWhitelisted(packageName);
+        return hasRequestedAppOpPermission(
+                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName);
     }
 
     private boolean hasRequestedAppOpPermission(String permission, String packageName) {
@@ -540,6 +537,17 @@
         return isCrossProfilePackageWhitelisted(packageName);
     }
 
+    @Override
+    public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+        final int[] profileIds = mInjector.getUserManager().getProfileIds(
+                mInjector.getCallingUserId(), /* enabledOnly= */ false);
+        if (profileIds.length < 2) {
+            return false;
+        }
+        return hasRequestedAppOpPermission(
+                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName);
+    }
+
     private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) {
         return mInjector.withCleanCallingIdentity(() -> {
             final int[] profileIds =
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79f75a..e605eeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -161,6 +161,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -178,6 +179,7 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ModuleInfo;
+import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
@@ -812,6 +814,10 @@
 
     private final OverlayConfig mOverlayConfig;
 
+    @GuardedBy("itself")
+    final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+        new ArrayList<>();
+
     /**
      * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
      *
@@ -5064,15 +5070,16 @@
      * action and a {@code android.intent.category.BROWSABLE} category</li>
      * </ul>
      */
-    int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+    int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+            boolean matchSystemOnly) {
         return updateFlagsForResolve(flags, userId, callingUid,
-                wantInstantApps, false /*onlyExposedExplicitly*/);
+                wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
     }
 
     int updateFlagsForResolve(int flags, int userId, int callingUid,
-            boolean wantInstantApps, boolean onlyExposedExplicitly) {
+            boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
         // Safe mode means we shouldn't match any third-party components
-        if (mSafeMode) {
+        if (mSafeMode || matchSystemOnly) {
             flags |= PackageManager.MATCH_SYSTEM_ONLY;
         }
         if (getInstantAppPackageName(callingUid) != null) {
@@ -6164,7 +6171,8 @@
 
             if (!mUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
-            flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+            flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+                    intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
             mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                     false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
 
@@ -6196,7 +6204,8 @@
         intent = updateIntentForResolve(intent);
         final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
         final int flags = updateFlagsForResolve(
-                0, userId, callingUid, false /*includeInstantApps*/);
+                0, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
                 userId);
         synchronized (mLock) {
@@ -6517,7 +6526,8 @@
                 android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                         android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
         flags = updateFlagsForResolve(
-                flags, userId, callingUid, false /*includeInstantApps*/);
+                flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         intent = updateIntentForResolve(intent);
         // writer
         synchronized (mLock) {
@@ -6729,7 +6739,8 @@
             }
             synchronized (mLock) {
                 int flags = updateFlagsForResolve(0, parent.id, callingUid,
-                        false /*includeInstantApps*/);
+                        false /*includeInstantApps*/,
+                        intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
                 CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
                         intent, resolvedType, flags, sourceUserId, parent.id);
                 return xpDomainInfo != null;
@@ -6815,7 +6826,8 @@
         }
 
         flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
-                comp != null || pkgName != null /*onlyExposedExplicitly*/);
+                comp != null || pkgName != null /*onlyExposedExplicitly*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7600,7 +7612,8 @@
             String resolvedType, int flags, int userId) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent activity options");
@@ -7786,7 +7799,8 @@
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -7876,7 +7890,8 @@
     private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
             int userId, int callingUid) {
         if (!mUserManager.exists(userId)) return null;
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* matchSystemOnly */);
         List<ResolveInfo> query = queryIntentServicesInternal(
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
         if (query != null) {
@@ -7907,7 +7922,8 @@
                 false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+        flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+                false /* matchSystemOnly */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -8044,7 +8060,8 @@
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* matchSystemOnly */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -16458,9 +16475,56 @@
             // BackgroundDexOptService will remove it from its blacklist.
             // TODO: Layering violation
             BackgroundDexOptService.notifyPackageChanged(packageName);
+
+            notifyPackageChangeObserversOnUpdate(reconciledPkg);
         }
     }
 
+    private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+      final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
+      final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
+      final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
+
+      PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+      pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
+      pkgChangeEvent.version = pkgSetting.versionCode;
+      pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+      pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
+      pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
+      pkgChangeEvent.isDeleted = false;
+
+      notifyPackageChangeObservers(pkgChangeEvent);
+    }
+
+    private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
+      PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+      pkgChangeEvent.packageName = packageName;
+      pkgChangeEvent.version = version;
+      pkgChangeEvent.lastUpdateTimeMillis = 0L;
+      pkgChangeEvent.newInstalled = false;
+      pkgChangeEvent.dataRemoved = false;
+      pkgChangeEvent.isDeleted = true;
+
+      notifyPackageChangeObservers(pkgChangeEvent);
+    }
+
+    private void notifyPackageChangeObservers(PackageChangeEvent event) {
+      try {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
+        synchronized (mPackageChangeObservers) {
+          for(IPackageChangeObserver observer : mPackageChangeObservers) {
+            try {
+              observer.onPackageChanged(event);
+            } catch(RemoteException e) {
+              Log.wtf(TAG, e);
+            }
+          }
+        }
+      } finally {
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+      }
+    }
+
     /**
      * The set of data needed to successfully install the prepared package. This includes data that
      * will be used to scan and reconcile the package.
@@ -17521,6 +17585,7 @@
             } catch (RemoteException e) {
                 Log.i(TAG, "Observer no longer exists.");
             } //end catch
+            notifyPackageChangeObserversOnDelete(packageName, versionCode);
         });
     }
 
@@ -22980,8 +23045,49 @@
         }
     }
 
+    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+        private final IPackageChangeObserver mObserver;
+
+        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+            mObserver = observer;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mPackageChangeObservers) {
+                mPackageChangeObservers.remove(mObserver);
+                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+                    + mPackageChangeObservers.size());
+            }
+        }
+    }
+
     private class PackageManagerNative extends IPackageManagerNative.Stub {
         @Override
+        public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+          synchronized (mPackageChangeObservers) {
+            try {
+                observer.asBinder().linkToDeath(
+                    new PackageChangeObserverDeathRecipient(observer), 0);
+            } catch (RemoteException e) {
+              Log.e(TAG, e.getMessage());
+            }
+            mPackageChangeObservers.add(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+                + mPackageChangeObservers.size());
+          }
+        }
+
+        @Override
+        public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+          synchronized (mPackageChangeObservers) {
+            mPackageChangeObservers.remove(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+                + mPackageChangeObservers.size());
+          }
+        }
+
+        @Override
         public String[] getAllPackages() {
             return PackageManagerService.this.getAllPackages().toArray(new String[0]);
         }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 161f304..27288d8 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -30,6 +30,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -47,6 +48,7 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.permission.PermissionControllerManager;
+import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.TelecomManager;
 import android.util.ArrayMap;
@@ -70,7 +72,9 @@
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -180,8 +184,6 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         intentFilter.addDataScheme("package");
 
-
-        /* TODO ntmyren: enable receiver when test flakes are fixed
         getContext().registerReceiverAsUser(new BroadcastReceiver() {
             final List<Integer> mUserSetupUids = new ArrayList<>(200);
             final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
@@ -232,7 +234,6 @@
                 manager.updateUserSensitiveForApp(uid);
             }
         }, UserHandle.ALL, intentFilter, null, null);
-         */
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b1c40cc..5025835 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -241,7 +241,7 @@
 
     private final Context mContext;
     private final ServiceThread mHandlerThread;
-    private final PowerManagerHandler mHandler;
+    private final Handler mHandler;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     private final BatterySaverController mBatterySaverController;
     private final BatterySaverPolicy mBatterySaverPolicy;
@@ -252,6 +252,7 @@
     private final LocalService mLocalService;
     private final NativeWrapper mNativeWrapper;
     private final SystemPropertiesWrapper mSystemProperties;
+    private final Clock mClock;
     private final Injector mInjector;
 
     private LightsManager mLightsManager;
@@ -597,7 +598,7 @@
 
         @Override
         public void onForegroundProfileSwitch(@UserIdInt int newProfileId) throws RemoteException {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             synchronized (mLock) {
                 mForegroundProfile = newProfileId;
                 maybeUpdateForegroundProfileLastActivityLocked(now);
@@ -625,11 +626,11 @@
         // Whether profile has been locked last time it timed out.
         boolean mLockingNotified;
 
-        public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) {
+        public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout, long now) {
             mUserId = userId;
             mScreenOffTimeout = screenOffTimeout;
             // Not accurate but at least won't cause immediate locking of the profile.
-            mLastUserActivityTime = SystemClock.uptimeMillis();
+            mLastUserActivityTime = now;
         }
     }
 
@@ -756,6 +757,15 @@
         }
     }
 
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
     @VisibleForTesting
     static class Injector {
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
@@ -822,6 +832,17 @@
             };
         }
 
+        Clock createClock() {
+            return SystemClock::uptimeMillis;
+        }
+
+        /**
+         * Handler for asynchronous operations performed by the power manager.
+         */
+        Handler createHandler(Looper looper, Handler.Callback callback) {
+            return new Handler(looper, callback, true /*async*/);
+        }
+
         void invalidateIsInteractiveCaches() {
             PowerManager.invalidateIsInteractiveCaches();
         }
@@ -853,12 +874,14 @@
         mLocalService = new LocalService();
         mNativeWrapper = injector.createNativeWrapper();
         mSystemProperties = injector.createSystemPropertiesWrapper();
+        mClock = injector.createClock();
         mInjector = injector;
 
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
         mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+        mHandler = injector.createHandler(mHandlerThread.getLooper(),
+                new PowerManagerHandlerCallback());
         mConstants = new Constants(mHandler);
         mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
         mAmbientDisplaySuppressionController =
@@ -1002,7 +1025,7 @@
                 incrementBootCount();
 
             } else if (phase == PHASE_BOOT_COMPLETED) {
-                final long now = SystemClock.uptimeMillis();
+                final long now = mClock.uptimeMillis();
                 mBootCompleted = true;
                 mDirty |= DIRTY_BOOT_COMPLETED;
 
@@ -1011,7 +1034,7 @@
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
                 if (sQuiescent) {
-                    goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+                    goToSleepNoUpdateLocked(mClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
                 }
@@ -1354,7 +1377,7 @@
                 opPackageName = wakeLock.mPackageName;
                 opUid = wakeLock.mOwnerUid;
             }
-            wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
+            wakeUpNoUpdateLocked(mClock.uptimeMillis(),
                     PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
                     opUid, opPackageName, opUid);
         }
@@ -1420,7 +1443,7 @@
     private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
         if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
                 && isScreenLock(wakeLock)) {
-            userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+            userActivityNoUpdateLocked(mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_OTHER,
                     PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
                     wakeLock.mOwnerUid);
@@ -1484,7 +1507,7 @@
     }
 
     private void restartNofifyLongTimerLocked(WakeLock wakeLock) {
-        wakeLock.mAcquireTime = SystemClock.uptimeMillis();
+        wakeLock.mAcquireTime = mClock.uptimeMillis();
         if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                 == PowerManager.PARTIAL_WAKE_LOCK && mNotifyLongScheduled == 0) {
             enqueueNotifyLongMsgLocked(wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL);
@@ -1569,7 +1592,7 @@
 
     private void onUserAttention() {
         synchronized (mLock) {
-            if (userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+            if (userActivityNoUpdateLocked(mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
                     Process.SYSTEM_UID)) {
                 updatePowerStateLocked();
@@ -1844,7 +1867,7 @@
      * had the system not been told the user was inactive.
      */
     private void logSleepTimeoutRecapturedLocked() {
-        final long now = SystemClock.uptimeMillis();
+        final long now = mClock.uptimeMillis();
         final long savedWakeTimeMs = mOverriddenTimeout - now;
         if (savedWakeTimeMs >= 0) {
             EventLogTags.writePowerSoftSleepRequested(savedWakeTimeMs);
@@ -1867,7 +1890,7 @@
             }
             if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
                 Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
-                final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
+                final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime);
                 if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
                     Slog.w(TAG, "Screen on took " + latencyMs + " ms");
                 }
@@ -1903,7 +1926,7 @@
             // Phase 1: Update wakefulness.
             // Loop because the wake lock and user activity computations are influenced
             // by changes in wakefulness.
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             int dirtyPhase2 = 0;
             for (;;) {
                 int dirtyPhase1 = mDirty;
@@ -1996,7 +2019,7 @@
                 // and it shuts off right away.
                 // Some devices also wake the device when plugged or unplugged because
                 // they don't have a charging LED.
-                final long now = SystemClock.uptimeMillis();
+                final long now = mClock.uptimeMillis();
                 if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                         dockedOnWirelessCharger)) {
                     wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
@@ -2204,7 +2227,7 @@
 
     void checkForLongWakeLocks() {
         synchronized (mLock) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             mNotifyLongDispatched = now;
             final long when = now - MIN_LONG_WAKE_CHECK_INTERVAL;
             long nextCheckTime = Long.MAX_VALUE;
@@ -2533,7 +2556,7 @@
                 if (DEBUG_SPEW) {
                     Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
                 }
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 if (isAttentiveTimeoutExpired(time)) {
                     changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
@@ -2568,7 +2591,7 @@
             return false;
         }
 
-        long now = SystemClock.uptimeMillis();
+        long now = mClock.uptimeMillis();
         if (isAttentiveTimeoutExpired(now)) {
             return !isBeingKeptFromInattentiveSleepLocked();
         } else {
@@ -2699,7 +2722,7 @@
             }
 
             // Determine whether the dream should continue.
-            long now = SystemClock.uptimeMillis();
+            long now = mClock.uptimeMillis();
             if (wakefulness == WAKEFULNESS_DREAMING) {
                 if (isDreaming && canDreamLocked()) {
                     if (mDreamsBatteryLevelDrainCutoffConfig >= 0
@@ -2881,7 +2904,7 @@
     private void updateScreenBrightnessBoostLocked(int dirty) {
         if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {
             if (mScreenBrightnessBoostInProgress) {
-                final long now = SystemClock.uptimeMillis();
+                final long now = mClock.uptimeMillis();
                 mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
                 if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
                     final long boostTimeout = mLastScreenBrightnessBoostTime +
@@ -2968,7 +2991,7 @@
             synchronized (mLock) {
                 mProximityPositive = false;
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
-                userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+                userActivityNoUpdateLocked(mClock.uptimeMillis(),
                         PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
@@ -3289,7 +3312,8 @@
                 if (profile != null) {
                     profile.mScreenOffTimeout = timeMs;
                 } else {
-                    mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs));
+                    mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs,
+                            mClock.uptimeMillis()));
                     // We need to recalculate wake locks for the new profile state.
                     mDirty |= DIRTY_WAKE_LOCKS;
                 }
@@ -3646,14 +3670,14 @@
     @VisibleForTesting
     boolean wasDeviceIdleForInternal(long ms) {
         synchronized (mLock) {
-            return mLastUserActivityTime + ms < SystemClock.uptimeMillis();
+            return mLastUserActivityTime + ms < mClock.uptimeMillis();
         }
     }
 
     @VisibleForTesting
     void onUserActivity() {
         synchronized (mLock) {
-            mLastUserActivityTime = SystemClock.uptimeMillis();
+            mLastUserActivityTime = mClock.uptimeMillis();
         }
     }
 
@@ -3662,7 +3686,7 @@
             synchronized (mLock) {
                 mForceSuspendActive = true;
                 // Place the system in an non-interactive state
-                goToSleepInternal(SystemClock.uptimeMillis(),
+                goToSleepInternal(mClock.uptimeMillis(),
                         PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
                         PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
 
@@ -3777,21 +3801,21 @@
             if (mNotifyLongScheduled == 0) {
                 pw.print("(none)");
             } else {
-                TimeUtils.formatDuration(mNotifyLongScheduled, SystemClock.uptimeMillis(), pw);
+                TimeUtils.formatDuration(mNotifyLongScheduled, mClock.uptimeMillis(), pw);
             }
             pw.println();
             pw.print("  mNotifyLongDispatched=");
             if (mNotifyLongDispatched == 0) {
                 pw.print("(none)");
             } else {
-                TimeUtils.formatDuration(mNotifyLongDispatched, SystemClock.uptimeMillis(), pw);
+                TimeUtils.formatDuration(mNotifyLongDispatched, mClock.uptimeMillis(), pw);
             }
             pw.println();
             pw.print("  mNotifyLongNextCheck=");
             if (mNotifyLongNextCheck == 0) {
                 pw.print("(none)");
             } else {
-                TimeUtils.formatDuration(mNotifyLongNextCheck, SystemClock.uptimeMillis(), pw);
+                TimeUtils.formatDuration(mNotifyLongNextCheck, mClock.uptimeMillis(), pw);
             }
             pw.println();
             pw.println("  mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
@@ -4389,15 +4413,11 @@
     };
 
     /**
-     * Handler for asynchronous operations performed by the power manager.
+     * Callback for asynchronous operations performed by the power manager.
      */
-    private final class PowerManagerHandler extends Handler {
-        public PowerManagerHandler(Looper looper) {
-            super(looper, null, true /*async*/);
-        }
-
+    private final class PowerManagerHandlerCallback implements Handler.Callback {
         @Override
-        public void handleMessage(Message msg) {
+        public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_USER_ACTIVITY_TIMEOUT:
                     handleUserActivityTimeout();
@@ -4415,6 +4435,8 @@
                     handleAttentiveTimeout();
                     break;
             }
+
+            return true;
         }
     }
 
@@ -4505,7 +4527,7 @@
             }
             if (mNotifiedAcquired) {
                 sb.append(" ACQ=");
-                TimeUtils.formatDuration(mAcquireTime-SystemClock.uptimeMillis(), sb);
+                TimeUtils.formatDuration(mAcquireTime-mClock.uptimeMillis(), sb);
             }
             if (mNotifiedLong) {
                 sb.append(" LONG");
@@ -4817,7 +4839,7 @@
 
         @Override // Binder call
         public void userActivity(long eventTime, int event, int flags) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
                     != PackageManager.PERMISSION_GRANTED
                     && mContext.checkCallingOrSelfPermission(
@@ -4855,7 +4877,7 @@
         @Override // Binder call
         public void wakeUp(long eventTime, @WakeReason int reason, String details,
                 String opPackageName) {
-            if (eventTime > SystemClock.uptimeMillis()) {
+            if (eventTime > mClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
 
@@ -4873,7 +4895,7 @@
 
         @Override // Binder call
         public void goToSleep(long eventTime, int reason, int flags) {
-            if (eventTime > SystemClock.uptimeMillis()) {
+            if (eventTime > mClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
 
@@ -4891,7 +4913,7 @@
 
         @Override // Binder call
         public void nap(long eventTime) {
-            if (eventTime > SystemClock.uptimeMillis()) {
+            if (eventTime > mClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
 
@@ -5286,7 +5308,7 @@
 
         @Override // Binder call
         public void boostScreenBrightness(long eventTime) {
-            if (eventTime > SystemClock.uptimeMillis()) {
+            if (eventTime > mClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
 
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 63048f6..a9e8d3f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -34,6 +34,7 @@
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -139,6 +140,14 @@
             throw new ServiceSpecificException(((RecoverableException) e).errorCode,
                     e.getMessage());
         }
+
+        /* Throwing an exception is not enough in this case. When the HAL behaves unexpectedly, the
+           system service and the HAL must be reset and the client must be notified. Without a full
+           reset in this catastrophic case, the state of the HAL and the system service cannot be
+           guaranteed to the client.
+         */
+        Log.wtf(TAG, "Crashing system server due to unrecoverable exception", e);
+        Process.killProcess(Process.myPid());
         throw new InternalServerError(e);
     }
 
@@ -377,7 +386,7 @@
             mCallback = callback;
             mHandle = handle;
             try {
-                mCallback.asBinder().linkToDeath(null, 0);
+                mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
@@ -680,7 +689,7 @@
             try {
                 mDelegate.detach();
                 mDelegate = null;
-                mCallback.asBinder().unlinkToDeath(null, 0);
+                mCallback.asBinder().unlinkToDeath(this, 0);
                 mModules.get(mHandle).remove(this);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e133c0fa..5e1be51 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -18,6 +18,7 @@
 
 import static android.app.AppOpsManager.OP_FLAG_SELF;
 import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
+import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.os.Debug.getIonHeapsSizeKb;
@@ -34,6 +35,9 @@
 import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
 import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
@@ -61,12 +65,13 @@
 import android.hardware.health.V2_0.IHealth;
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.net.NetworkStats;
+import android.net.NetworkTemplate;
 import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
-import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -200,7 +205,8 @@
 
     private final Object mNetworkStatsLock = new Object();
     @GuardedBy("mNetworkStatsLock")
-    private INetworkStatsService mNetworkStatsService;
+    @Nullable
+    private INetworkStatsSession mNetworkStatsSession;
 
     private final Object mThermalLock = new Object();
     @GuardedBy("mThermalLock")
@@ -289,13 +295,13 @@
             try {
                 switch (atomTag) {
                     case FrameworkStatsLog.WIFI_BYTES_TRANSFER:
-                        return pullWifiBytesTransfer(atomTag, data);
+                        return pullWifiBytesTransfer(atomTag, data, false);
                     case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
-                        return pullWifiBytesTransferBackground(atomTag, data);
+                        return pullWifiBytesTransfer(atomTag, data, true);
                     case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
-                        return pullMobileBytesTransfer(atomTag, data);
+                        return pullMobileBytesTransfer(atomTag, data, false);
                     case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
-                        return pullMobileBytesTransferBackground(atomTag, data);
+                        return pullMobileBytesTransfer(atomTag, data, true);
                     case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER:
                         return pullBluetoothBytesTransfer(atomTag, data);
                     case FrameworkStatsLog.KERNEL_WAKELOCK:
@@ -574,26 +580,35 @@
         registerBatteryCycleCount();
     }
 
-    private INetworkStatsService getINetworkStatsService() {
+    /**
+     * Return the {@code INetworkStatsSession} object that holds the necessary properties needed
+     * for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or
+     * null if the service or binder cannot be obtained.
+     */
+    @Nullable
+    private INetworkStatsSession getNetworkStatsSession() {
         synchronized (mNetworkStatsLock) {
-            if (mNetworkStatsService == null) {
-                mNetworkStatsService = INetworkStatsService.Stub.asInterface(
-                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-                if (mNetworkStatsService != null) {
-                    try {
-                        mNetworkStatsService.asBinder().linkToDeath(() -> {
-                            synchronized (mNetworkStatsLock) {
-                                mNetworkStatsService = null;
-                            }
-                        }, /* flags */ 0);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "linkToDeath with NetworkStatsService failed", e);
-                        mNetworkStatsService = null;
-                    }
-                }
+            if (mNetworkStatsSession != null) return mNetworkStatsSession;
 
+            final INetworkStatsService networkStatsService =
+                    INetworkStatsService.Stub.asInterface(
+                            ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            if (networkStatsService == null) return null;
+
+            try {
+                networkStatsService.asBinder().linkToDeath(() -> {
+                    synchronized (mNetworkStatsLock) {
+                        mNetworkStatsSession = null;
+                    }
+                }, /* flags */ 0);
+                mNetworkStatsSession = networkStatsService.openSessionForUsageStats(
+                        FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Cannot get NetworkStats session", e);
+                mNetworkStatsSession = null;
             }
-            return mNetworkStatsService;
+
+            return mNetworkStatsSession;
         }
     }
 
@@ -698,42 +713,38 @@
         );
     }
 
-    int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
-        INetworkStatsService networkStatsService = getINetworkStatsService();
-        if (networkStatsService == null) {
-            Slog.e(TAG, "NetworkStats Service is not available!");
-            return StatsManager.PULL_SKIP;
+    private int pullWifiBytesTransfer(
+            int atomTag, @NonNull List<StatsEvent> pulledData, boolean withFgbg) {
+        final NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
+        final NetworkStats stats = getUidNetworkStatsSinceBoot(template, withFgbg);
+        if (stats != null) {
+            addNetworkStats(atomTag, pulledData, stats, withFgbg);
+            return StatsManager.PULL_SUCCESS;
         }
-        long token = Binder.clearCallingIdentity();
-        try {
-            // TODO(b/148402814): Consider caching the following call to get BatteryStatsInternal.
-            BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
-            String[] ifaces = bs.getWifiIfaces();
-            if (ifaces.length == 0) {
-                return StatsManager.PULL_SKIP;
-            }
-            // Combine all the metrics per Uid into one record.
-            NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid();
-            addNetworkStats(atomTag, pulledData, stats, false);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
-            return StatsManager.PULL_SKIP;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return StatsManager.PULL_SUCCESS;
+        return StatsManager.PULL_SKIP;
     }
 
-    private void addNetworkStats(
-            int tag, List<StatsEvent> ret, NetworkStats stats, boolean withFGBG) {
+    private int pullMobileBytesTransfer(
+            int atomTag, @NonNull List<StatsEvent> pulledData, boolean withFgbg) {
+        final NetworkTemplate template = NetworkTemplate.buildTemplateMobileWildcard();
+        final NetworkStats stats = getUidNetworkStatsSinceBoot(template, withFgbg);
+        if (stats != null) {
+            addNetworkStats(atomTag, pulledData, stats, withFgbg);
+            return StatsManager.PULL_SUCCESS;
+        }
+        return StatsManager.PULL_SKIP;
+    }
+
+    private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
+            @NonNull NetworkStats stats, boolean withFgbg) {
         int size = stats.size();
-        NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+        final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
         for (int j = 0; j < size; j++) {
             stats.getValues(j, entry);
             StatsEvent.Builder e = StatsEvent.newBuilder();
-            e.setAtomId(tag);
+            e.setAtomId(atomTag);
             e.writeInt(entry.uid);
-            if (withFGBG) {
+            if (withFgbg) {
                 e.writeInt(entry.set);
             }
             e.writeLong(entry.rxBytes);
@@ -744,11 +755,27 @@
         }
     }
 
+    @Nullable private NetworkStats getUidNetworkStatsSinceBoot(
+            @NonNull NetworkTemplate template, boolean withFgbg) {
+
+        final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
+        final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
+        try {
+            final NetworkStats stats = getNetworkStatsSession().getSummaryForAllUid(template,
+                    currentTimeInMillis - elapsedMillisSinceBoot, currentTimeInMillis, false);
+            return withFgbg ? rollupNetworkStatsByFgbg(stats) : stats.groupedByUid();
+        } catch (RemoteException | NullPointerException e) {
+            Slog.e(TAG, "Pulling netstats for " + template
+                    + " fgbg= " + withFgbg + " bytes has error", e);
+        }
+        return null;
+    }
+
     /**
      * Allows rollups per UID but keeping the set (foreground/background) slicing.
      * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
      */
-    private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
+    @NonNull private NetworkStats rollupNetworkStatsByFgbg(@NonNull NetworkStats stats) {
         final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
 
         final NetworkStats.Entry entry = new NetworkStats.Entry();
@@ -758,7 +785,7 @@
         entry.roaming = NetworkStats.ROAMING_ALL;
 
         int size = stats.size();
-        NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
+        final NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
         for (int i = 0; i < size; i++) {
             stats.getValues(i, recycle);
 
@@ -790,31 +817,6 @@
         );
     }
 
-    int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
-        INetworkStatsService networkStatsService = getINetworkStatsService();
-        if (networkStatsService == null) {
-            Slog.e(TAG, "NetworkStats Service is not available!");
-            return StatsManager.PULL_SKIP;
-        }
-        long token = Binder.clearCallingIdentity();
-        try {
-            BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
-            String[] ifaces = bs.getWifiIfaces();
-            if (ifaces.length == 0) {
-                return StatsManager.PULL_SKIP;
-            }
-            NetworkStats stats = rollupNetworkStatsByFGBG(
-                    networkStatsService.getDetailedUidStats(ifaces));
-            addNetworkStats(atomTag, pulledData, stats, true);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
-            return StatsManager.PULL_SKIP;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return StatsManager.PULL_SUCCESS;
-    }
-
     private void registerMobileBytesTransfer() {
         int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
@@ -828,31 +830,6 @@
         );
     }
 
-    int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
-        INetworkStatsService networkStatsService = getINetworkStatsService();
-        if (networkStatsService == null) {
-            Slog.e(TAG, "NetworkStats Service is not available!");
-            return StatsManager.PULL_SKIP;
-        }
-        long token = Binder.clearCallingIdentity();
-        try {
-            BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
-            String[] ifaces = bs.getMobileIfaces();
-            if (ifaces.length == 0) {
-                return StatsManager.PULL_SKIP;
-            }
-            // Combine all the metrics per Uid into one record.
-            NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid();
-            addNetworkStats(atomTag, pulledData, stats, false);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
-            return StatsManager.PULL_SKIP;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return StatsManager.PULL_SUCCESS;
-    }
-
     private void registerMobileBytesTransferBackground() {
         int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
@@ -866,31 +843,6 @@
         );
     }
 
-    int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
-        INetworkStatsService networkStatsService = getINetworkStatsService();
-        if (networkStatsService == null) {
-            Slog.e(TAG, "NetworkStats Service is not available!");
-            return StatsManager.PULL_SKIP;
-        }
-        long token = Binder.clearCallingIdentity();
-        try {
-            BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
-            String[] ifaces = bs.getMobileIfaces();
-            if (ifaces.length == 0) {
-                return StatsManager.PULL_SKIP;
-            }
-            NetworkStats stats = rollupNetworkStatsByFGBG(
-                    networkStatsService.getDetailedUidStats(ifaces));
-            addNetworkStats(atomTag, pulledData, stats, true);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
-            return StatsManager.PULL_SKIP;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return StatsManager.PULL_SUCCESS;
-    }
-
     private void registerBluetoothBytesTransfer() {
         int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e8bfe8e..fb22481 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -36,7 +36,6 @@
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -3781,20 +3780,6 @@
         return super.checkCompleteDeferredRemoval();
     }
 
-    @Override
-    int getOrientation() {
-        return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
-    }
-
-    private boolean canSpecifyOrientation() {
-        final int windowingMode = getWindowingMode();
-        final int activityType = getActivityType();
-        return windowingMode == WINDOWING_MODE_FULLSCREEN
-                || activityType == ACTIVITY_TYPE_HOME
-                || activityType == ACTIVITY_TYPE_RECENTS
-                || activityType == ACTIVITY_TYPE_ASSISTANT;
-    }
-
     public DisplayInfo getDisplayInfo() {
         return mDisplayContent.getDisplayInfo();
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index edc87e5..2263795 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -548,8 +548,11 @@
 
     /**
      * Gets bitmap snapshot of the provided task id.
+     *
+     * <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency
+     * sensitive environment.
      */
-    public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
+    public abstract ActivityManager.TaskSnapshot getTaskSnapshotBlocking(int taskId,
             boolean isLowResolution);
 
     /** Returns true if uid is considered foreground for activity start purposes. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f7278e7..214a676 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7477,10 +7477,10 @@
         }
 
         @Override
-        public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
-                boolean isLowResolution) {
+        public ActivityManager.TaskSnapshot getTaskSnapshotBlocking(
+                int taskId, boolean isLowResolution) {
             return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
-                    false /* restoreFromDisk */);
+                    true /* restoreFromDisk */);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index d9e41af..8cac487 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -21,6 +21,9 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_ROOT;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
 
 import static com.android.internal.util.Preconditions.checkState;
 import static com.android.server.wm.DisplayAreaProto.NAME;
@@ -28,13 +31,16 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.window.IDisplayAreaOrganizer;
 
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 
 import java.util.Comparator;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -57,13 +63,24 @@
 
     protected final Type mType;
     private final String mName;
+    final int mFeatureId;
+    private final DisplayAreaOrganizerController mOrganizerController;
+    IDisplayAreaOrganizer mOrganizer;
 
     DisplayArea(WindowManagerService wms, Type type, String name) {
+        this(wms, type, name, FEATURE_UNDEFINED);
+    }
+
+    DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
         super(wms);
         // TODO(display-area): move this up to ConfigurationContainer
         mOrientation = SCREEN_ORIENTATION_UNSET;
         mType = type;
         mName = name;
+        mFeatureId = featureId;
+        mRemoteToken = new RemoteToken(this);
+        mOrganizerController =
+                wms.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
     }
 
     @Override
@@ -116,6 +133,33 @@
         return DISPLAY_AREA;
     }
 
+    void forAllDisplayAreas(Consumer<DisplayArea> callback) {
+        super.forAllDisplayAreas(callback);
+        callback.accept(this);
+    }
+
+    void setOrganizer(IDisplayAreaOrganizer organizer) {
+        if (mOrganizer == organizer) return;
+        sendDisplayAreaVanished();
+        mOrganizer = organizer;
+        sendDisplayAreaAppeared();
+    }
+
+    void sendDisplayAreaAppeared() {
+        if (mOrganizer == null) return;
+        mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
+    }
+
+    void sendDisplayAreaVanished() {
+        if (mOrganizer == null) return;
+        mOrganizerController.onDisplayAreaVanished(mOrganizer, this);
+    }
+
+    @Override
+    boolean isOrganized() {
+        return mOrganizer != null;
+    }
+
     /**
      * DisplayArea that contains WindowTokens, and orders them according to their type.
      */
@@ -152,7 +196,7 @@
         };
 
         Tokens(WindowManagerService wms, Type type, String name) {
-            super(wms, type, name);
+            super(wms, type, name, FEATURE_WINDOW_TOKENS);
         }
 
         void addChild(WindowToken token) {
@@ -191,7 +235,7 @@
         private final Rect mTmpDimBoundsRect = new Rect();
 
         Root(WindowManagerService wms) {
-            super(wms, Type.ANY, "DisplayArea.Root");
+            super(wms, Type.ANY, "DisplayArea.Root", FEATURE_ROOT);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
new file mode 100644
index 0000000..464b127
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.IDisplayAreaOrganizer;
+import android.window.IDisplayAreaOrganizerController;
+
+import java.util.HashMap;
+
+public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
+    private static final String TAG = "DisplayAreaOrganizerController";
+
+    final ActivityTaskManagerService mService;
+    private final WindowManagerGlobalLock mGlobalLock;
+    private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap();
+
+    private class DeathRecipient implements IBinder.DeathRecipient {
+        int mFeature;
+        IDisplayAreaOrganizer mOrganizer;
+
+        DeathRecipient(IDisplayAreaOrganizer organizer, int feature) {
+            mOrganizer = organizer;
+            mFeature = feature;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mGlobalLock) {
+                mOrganizersByFeatureIds.remove(mFeature);
+                mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+                    if (da.mOrganizer != mOrganizer) return;
+                    da.setOrganizer(null);
+                });
+            }
+        }
+    }
+
+    DisplayAreaOrganizerController(ActivityTaskManagerService atm) {
+        mService = atm;
+        mGlobalLock = atm.mGlobalLock;
+    }
+
+    private void enforceStackPermission(String func) {
+        mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
+    }
+
+    @Override
+    public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
+        enforceStackPermission("registerOrganizer()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (mOrganizersByFeatureIds.get(feature) != null) {
+                    throw new IllegalStateException(
+                            "Replacing existing organizer currently unsupported");
+                }
+
+                final DeathRecipient dr = new DeathRecipient(organizer, feature);
+                try {
+                    organizer.asBinder().linkToDeath(dr, 0);
+                } catch (RemoteException e) {
+                    // Oh well...
+                }
+                mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+                    if (da.mFeatureId != feature) return;
+                    da.setOrganizer(organizer);
+                });
+
+                mOrganizersByFeatureIds.put(feature, organizer);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        try {
+            organizer.onDisplayAreaAppeared(da.mRemoteToken);
+        } catch (RemoteException e) {
+            // Oh well...
+        }
+    }
+
+    void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        try {
+            organizer.onDisplayAreaVanished(da.mRemoteToken);
+        } catch (RemoteException e) {
+            // Oh well...
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 885456a..0c6e483 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -73,16 +73,36 @@
      */
     static class Feature {
         private final String mName;
+        private final int mId;
         private final boolean[] mWindowLayers;
 
-        private Feature(String name, boolean[] windowLayers) {
+        private Feature(String name, int id, boolean[] windowLayers) {
             mName = name;
+            mId = id;
             mWindowLayers = windowLayers;
         }
 
+        /**
+         * Returns the id of the feature.
+         *
+         * Must be unique among the features added to a {@link DisplayAreaPolicyBuilder}.
+         *
+         * @see android.window.WindowOrganizer.DisplayAreaOrganizer#FEATURE_SYSTEM_FIRST
+         * @see android.window.WindowOrganizer.DisplayAreaOrganizer#FEATURE_VENDOR_FIRST
+         */
+        public int getId() {
+            return mId;
+        }
+
+        @Override
+        public String toString() {
+            return "Feature(\"" + mName + "\", " + mId + '}';
+        }
+
         static class Builder {
             private final WindowManagerPolicy mPolicy;
             private final String mName;
+            private final int mId;
             private final boolean[] mLayers;
 
             /**
@@ -96,10 +116,12 @@
              * The builder starts out with the feature not applying to any types.
              *
              * @param name the name of the feature.
+             * @param id of the feature. {@see Feature#getId}
              */
-            Builder(WindowManagerPolicy policy, String name) {
+            Builder(WindowManagerPolicy policy, String name, int id) {
                 mPolicy = policy;
                 mName = name;
+                mId = id;
                 mLayers = new boolean[mPolicy.getMaxWindowLayer()];
             }
 
@@ -147,7 +169,7 @@
             }
 
             Feature build() {
-                return new Feature(mName, mLayers.clone());
+                return new Feature(mName, mId, mLayers.clone());
             }
 
             private void set(int type, boolean value) {
@@ -364,7 +386,7 @@
                 return leaf;
             } else {
                 return new DisplayArea(parent.mWmService, type, mFeature.mName + ":"
-                        + mMinLayer + ":" + mMaxLayer);
+                        + mMinLayer + ":" + mMaxLayer, mFeature.mId);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a20318d..7df731b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -79,6 +79,7 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -4294,7 +4295,7 @@
         private ActivityStack mRootSplitScreenPrimaryTask = null;
 
         TaskContainers(WindowManagerService service) {
-            super(service, Type.ANY, "TaskContainers");
+            super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 34acabe..5e88fb0 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -115,7 +115,6 @@
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -178,7 +177,6 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -333,9 +331,6 @@
                 }
             };
 
-    @GuardedBy("mHandler")
-    private SleepToken mDreamingSleepToken;
-
     // The windows we were told about in focusChanged.
     private WindowState mFocusedWindow;
     private WindowState mLastFocusedWindow;
@@ -394,7 +389,6 @@
     private boolean mShowingDream;
     private boolean mLastShowingDream;
     private boolean mDreamingLockscreen;
-    private boolean mDreamingSleepTokenNeeded;
     private boolean mAllowLockscreenWhenOn;
 
     private InputConsumer mInputConsumer = null;
@@ -414,7 +408,6 @@
     private RefreshRatePolicy mRefreshRatePolicy;
 
     // -------- PolicyHandler --------
-    private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
     private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
     private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
     private static final int MSG_ENABLE_POINTER_LOCATION = 4;
@@ -434,9 +427,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
-                    updateDreamingSleepToken(msg.arg1 != 0);
-                    break;
                 case MSG_REQUEST_TRANSIENT_BARS:
                     synchronized (mLock) {
                         WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
@@ -2638,15 +2628,6 @@
         // while the dream is showing.
         if (!mShowingDream) {
             mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
-            if (mDreamingSleepTokenNeeded) {
-                mDreamingSleepTokenNeeded = false;
-                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
-            }
-        } else {
-            if (!mDreamingSleepTokenNeeded) {
-                mDreamingSleepTokenNeeded = true;
-                mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
-            }
         }
 
         if (mStatusBar != null) {
@@ -3160,21 +3141,6 @@
         return !mShowingDream;
     }
 
-    private void updateDreamingSleepToken(boolean acquire) {
-        if (acquire) {
-            final int displayId = getDisplayId();
-            if (mDreamingSleepToken == null) {
-                mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken(
-                        "DreamOnDisplay" + displayId, displayId);
-            }
-        } else {
-            if (mDreamingSleepToken != null) {
-                mDreamingSleepToken.release();
-                mDreamingSleepToken = null;
-            }
-        }
-    }
-
     private void requestTransientBars(WindowState swipeTarget) {
         if (!mService.mPolicy.isUserSetupComplete()) {
             // Swipe-up for navigation bar is disabled during setup
@@ -3854,7 +3820,6 @@
         }
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
         pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
-        pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
         if (mStatusBar != null) {
             pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
         }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index e2c7d52..88cdd17 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -301,7 +301,7 @@
          * we may have some issues with modal-windows, but I guess we can
          * cross that bridge when we come to implementing full-screen TaskOrg
          */
-        if (child.getTask() != null && child.getTask().isControlledByTaskOrganizer()) {
+        if (child.getTask() != null && child.getTask().isOrganized()) {
             inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
         }
 
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 45f8a15..420997a 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.ActivityStack.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
 
 import android.app.ActivityOptions;
 import android.content.Intent;
@@ -233,29 +232,6 @@
         }
 
         final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
-        final ArrayList<Task> createdTasks = new ArrayList<>();
-        while (!mPendingReparentActivities.isEmpty()) {
-            final ActivityRecord r = mPendingReparentActivities.remove(0);
-            final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
-            final Task targetTask;
-            if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
-                // If the activity currently at the bottom has the same task affinity as
-                // the one we are moving, then merge it into the same task.
-                targetTask = bottom.getTask();
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                        + r + " out to bottom task " + targetTask);
-            } else {
-                targetTask = mTargetStack.reuseOrCreateTask(
-                        r.info, null /*intent*/, false /*toTop*/);
-                targetTask.affinityIntent = r.intent;
-                createdTasks.add(targetTask);
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                        + r + " out to new task " + targetTask);
-            }
-            r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
-            atmService.mStackSupervisor.mRecentTasks.add(targetTask);
-        }
-
         DisplayContent display = mTargetStack.getDisplay();
         final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
         if (singleTaskInstanceDisplay) {
@@ -264,16 +240,33 @@
 
         final int windowingMode = mTargetStack.getWindowingMode();
         final int activityType = mTargetStack.getActivityType();
-        if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
-            return;
-        }
 
-        while (!createdTasks.isEmpty()) {
-            final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
-            final ActivityStack targetStack = display.getOrCreateStack(
-                    windowingMode, activityType, false /* onTop */);
-            targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
-                    false /* animate */, true /* deferResume */, "resetTargetTask");
+        while (!mPendingReparentActivities.isEmpty()) {
+            final ActivityRecord r = mPendingReparentActivities.remove(0);
+            final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+                    activityType);
+            final Task task = alwaysCreateTask
+                    ? display.getBottomMostTask() : mTargetStack.getBottomMostTask();
+            Task targetTask = null;
+            if (task != null && r.taskAffinity.equals(task.affinity)) {
+                // If the activity currently at the bottom has the same task affinity as
+                // the one we are moving, then merge it into the same task.
+                targetTask = task;
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+                        + r + " out to bottom task " + targetTask);
+            }
+            if (targetTask == null) {
+                if (alwaysCreateTask) {
+                    targetTask = display.getOrCreateStack(windowingMode, activityType,
+                            false /* onTop */);
+                } else {
+                    targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
+                            false /*toTop*/);
+                }
+                targetTask.affinityIntent = r.intent;
+            }
+            r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+            atmService.mStackSupervisor.mRecentTasks.add(targetTask);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eca7d2e..bfe3147 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,9 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
@@ -129,11 +132,11 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.window.ITaskOrganizer;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.window.ITaskOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
@@ -509,22 +512,6 @@
                 _voiceSession, _voiceInteractor, stack);
     }
 
-    class TaskToken extends RemoteToken {
-        TaskToken(WindowContainer container) {
-            super(container);
-        }
-
-        @Override
-        public SurfaceControl getLeash() {
-            // We need to copy the SurfaceControl instead of returning the original
-            // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
-            // to release themselves.
-            SurfaceControl sc = new SurfaceControl();
-            sc.copyFrom(getSurfaceControl());
-            return sc;
-        }
-    }
-
     /** Don't use constructor directly. This is only used by XML parser. */
     Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent,
             String _affinity, String _rootAffinity, ComponentName _realActivity,
@@ -550,7 +537,7 @@
         mTaskDescription = _lastTaskDescription;
         // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
         setOrientation(SCREEN_ORIENTATION_UNSET);
-        mRemoteToken = new TaskToken(this);
+        mRemoteToken = new RemoteToken(this);
         affinityIntent = _affinityIntent;
         affinity = _affinity;
         rootAffinity = _rootAffinity;
@@ -594,6 +581,14 @@
         voiceInteractor = _voiceInteractor;
         setIntent(activity, intent, info);
         setMinDimensions(info);
+        // Before we began to reuse a root task (old ActivityStack) as the leaf task, we used to
+        // create a leaf task in this case. Therefore now we won't send out the task created
+        // notification when we decide to reuse it here, so we send out the notification below.
+        // The reason why the created notification sent out when root task is created doesn't work
+        // is that realActivity isn't set until setIntent() method above is called for the first
+        // time. Eventually this notification will be removed when we can populate those information
+        // when root task is created.
+        mAtmService.getTaskChangeNotificationController().notifyTaskCreated(mTaskId, realActivity);
         return this;
     }
 
@@ -602,11 +597,7 @@
             return;
         }
 
-        // TODO(xutan): Removed type check after stack and task is merged.
-        // Before the real merge of stack and task, we need to avoid saving state of stacks. Once
-        // the merge is finished we can just pass DisplayContent because both windowing mode and
-        // bounds are set in the merged task.
-        if (oldParent instanceof ActivityStack) {
+        if (isLeafTask()) {
             // This task is going away, so save the last state if necessary.
             saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
         }
@@ -3110,7 +3101,7 @@
         /**
          * Animations are handled by the TaskOrganizer implementation.
          */
-        if (isControlledByTaskOrganizer()) {
+        if (isOrganized()) {
             return false;
         }
         // Don't animate while the task runs recents animation but only if we are in the mode
@@ -3215,6 +3206,20 @@
     }
 
     @Override
+    int getOrientation(int candidate) {
+        return canSpecifyOrientation() ? super.getOrientation(candidate) : SCREEN_ORIENTATION_UNSET;
+    }
+
+    private boolean canSpecifyOrientation() {
+        final int windowingMode = getWindowingMode();
+        final int activityType = getActivityType();
+        return windowingMode == WINDOWING_MODE_FULLSCREEN
+                || activityType == ACTIVITY_TYPE_HOME
+                || activityType == ACTIVITY_TYPE_RECENTS
+                || activityType == ACTIVITY_TYPE_ASSISTANT;
+    }
+
+    @Override
     boolean fillsParent() {
         return matchParentBounds();
     }
@@ -4014,7 +4019,8 @@
         }
     }
 
-    boolean isControlledByTaskOrganizer() {
+    @Override
+    boolean isOrganized() {
         final Task rootTask = getRootTask();
         // if the rootTask is a "child" of a tile, then don't consider it a root task.
         // TODO: remove this along with removing tile.
@@ -4030,7 +4036,7 @@
          * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
          * Surface in to its own hierarchy.
          */
-        if (isControlledByTaskOrganizer()) {
+        if (isOrganized()) {
             return;
         }
         super.reparentSurfaceControl(t, newParent);
@@ -4117,27 +4123,6 @@
         sendTaskAppeared();
     }
 
-    @Override
-    public void updateSurfacePosition() {
-        // Avoid fighting with the TaskOrganizer over Surface position.
-        if (isControlledByTaskOrganizer()) {
-            return;
-        } else {
-            super.updateSurfacePosition();
-        }
-    }
-
-    @Override
-    void getRelativeDisplayedPosition(Point outPos) {
-        // In addition to updateSurfacePosition, we keep other code that sets
-        // position from fighting with the TaskOrganizer
-        if (isControlledByTaskOrganizer()) {
-            outPos.set(0, 0);
-            return;
-        }
-        super.getRelativeDisplayedPosition(outPos);
-    }
-
     /**
      * @return true if the task is currently focused.
      */
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fc58ee7..5523678 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -111,7 +111,7 @@
         void addTask(Task t) {
             mOrganizedTasks.add(t);
             try {
-                mOrganizer.taskAppeared(t.getTaskInfo());
+                mOrganizer.onTaskAppeared(t.getTaskInfo());
             } catch (Exception e) {
                 Slog.e(TAG, "Exception sending taskAppeared callback" + e);
             }
@@ -119,7 +119,7 @@
 
         void removeTask(Task t) {
             try {
-                mOrganizer.taskVanished(t.getTaskInfo());
+                mOrganizer.onTaskVanished(t.getTaskInfo());
             } catch (Exception e) {
                 Slog.e(TAG, "Exception sending taskVanished callback" + e);
             }
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 822f840..51142b1 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -218,7 +218,7 @@
 
     static TaskTile forToken(IBinder token) {
         try {
-            return (TaskTile) ((TaskToken) token).getContainer();
+            return (TaskTile) ((RemoteToken) token).getContainer();
         } catch (ClassCastException e) {
             Slog.w(TAG, "Bad tile token: " + token, e);
             return null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a49cffb..ae0b685 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1612,6 +1612,12 @@
         return null;
     }
 
+    void forAllDisplayAreas(Consumer<DisplayArea> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).forAllDisplayAreas(callback);
+        }
+    }
+
     /**
      * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
      * the input container in terms of z-order.
@@ -2325,6 +2331,9 @@
     }
 
     void updateSurfacePosition() {
+        // Avoid fighting with the organizer over Surface position.
+        if (isOrganized()) return;
+
         if (mSurfaceControl == null) {
             return;
         }
@@ -2374,6 +2383,13 @@
     }
 
     void getRelativeDisplayedPosition(Point outPos) {
+        // In addition to updateSurfacePosition, we keep other code that sets
+        // position from fighting with the organizer
+        if (isOrganized()) {
+            outPos.set(0, 0);
+            return;
+        }
+
         final Rect dispBounds = getDisplayedBounds();
         outPos.set(dispBounds.left, dispBounds.top);
         final WindowContainer parent = getParent();
@@ -2414,8 +2430,12 @@
         return null;
     }
 
-    RemoteToken getRemoteToken() {
-        return mRemoteToken;
+    /**
+     * @return {@code true} if window container is manage by a
+     *          {@link android.window.WindowOrganizer}
+     */
+    boolean isOrganized() {
+        return false;
     }
 
     static WindowContainer fromBinder(IBinder binder) {
@@ -2439,7 +2459,14 @@
 
         @Override
         public SurfaceControl getLeash() {
-            throw new RuntimeException("Not implemented");
+            final WindowContainer wc = getContainer();
+            if (wc == null) return null;
+            // We need to copy the SurfaceControl instead of returning the original
+            // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
+            // to release themselves.
+            SurfaceControl sc = new SurfaceControl();
+            sc.copyFrom(wc.getSurfaceControl());
+            return sc;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ebfe109..e416e80 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -33,6 +33,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.view.SurfaceControl;
+import android.window.IDisplayAreaOrganizerController;
 import android.window.ITaskOrganizerController;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.IWindowOrganizerController;
@@ -76,11 +77,13 @@
             mTransactionCallbacksByPendingSyncId = new HashMap();
 
     final TaskOrganizerController mTaskOrganizerController;
+    final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
 
     WindowOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
         mGlobalLock = atm.mGlobalLock;
         mTaskOrganizerController = new TaskOrganizerController(mService);
+        mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
     }
 
     @Override
@@ -318,6 +321,12 @@
         return mTaskOrganizerController;
     }
 
+    @Override
+    public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
+        enforceStackPermission("getDisplayAreaOrganizerController()");
+        return mDisplayAreaOrganizerController;
+    }
+
     int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
         int id = mBLASTSyncEngine.startSyncSet(this);
         mTransactionCallbacksByPendingSyncId.put(id, callback);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 740c5cb..96cdbb3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -136,7 +136,7 @@
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
-import android.app.admin.DevicePolicyManager.PersonalAppSuspensionReason;
+import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DeviceStateCache;
 import android.app.admin.FactoryResetProtectionPolicy;
@@ -935,10 +935,14 @@
                 }
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 handlePackagesChanged(null /* check all admins */, userHandle);
-            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
-                    || (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
+            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
                 handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
+                } else {
+                    handleNewPackageInstalled(intent.getData().getSchemeSpecificPart(), userHandle);
+                }
             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
                     && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                 handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
@@ -2028,6 +2032,26 @@
         return false;
     }
 
+    private void handleNewPackageInstalled(String packageName, int userHandle) {
+        // If personal apps were suspended by the admin, suspend the newly installed one.
+        if (!getUserData(userHandle).mAppsSuspended) {
+            return;
+        }
+        final String[] packagesToSuspend = { packageName };
+        // Check if package is considered not suspendable?
+        if (mInjector.getPackageManager(userHandle)
+                .getUnsuspendablePackages(packagesToSuspend).length != 0) {
+            Slog.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
+            return;
+        }
+        try {
+            mIPackageManager.setPackagesSuspendedAsUser(packagesToSuspend, true /*suspend*/,
+                    null, null, null, PLATFORM_PACKAGE_NAME, userHandle);
+        } catch (RemoteException ignored) {
+            // shouldn't happen.
+        }
+    }
+
     /**
      * Unit test will subclass it to inject mocks.
      */
@@ -2110,6 +2134,11 @@
             return mContext.getPackageManager();
         }
 
+        PackageManager getPackageManager(int userId) {
+            return mContext
+                    .createContextAsUser(UserHandle.of(userId), 0 /* flags */).getPackageManager();
+        }
+
         PowerManagerInternal getPowerManagerInternal() {
             return LocalServices.getService(PowerManagerInternal.class);
         }
@@ -13041,26 +13070,25 @@
             final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
 
-            UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
-            final boolean addingProfileRestrictedOnParent = (parentUser != null)
-                    && mUserManager.hasUserRestriction(
-                            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
-                            UserHandle.of(parentUser.id));
+            if (mUserManager.getUserInfo(callingUserId).isProfile()) {
+                Slog.i(LOG_TAG,
+                        String.format("Calling user %d is a profile, cannot add another.",
+                                callingUserId));
+                // The check is called from inside a managed profile. A managed profile cannot
+                // be provisioned from within another managed profile.
+                return CODE_CANNOT_ADD_MANAGED_PROFILE;
+            }
 
-            Slog.i(LOG_TAG, String.format(
-                    "When checking for managed profile provisioning: Has device owner? %b, adding"
-                            + " profile restricted? %b, adding profile restricted on parent? %b",
-                    hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
-
-            // If there's a device owner, the restriction on adding a managed profile must be set
-            // somewhere.
-            if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+            // If there's a device owner, the restriction on adding a managed profile must be set.
+            if (hasDeviceOwner && !addingProfileRestricted) {
                 Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
             }
 
-            // Do not allow adding a managed profile if there's a restriction, either on the current
-            // user or its parent user.
-            if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+            // Do not allow adding a managed profile if there's a restriction.
+            if (addingProfileRestricted) {
+                Slog.i(LOG_TAG, String.format(
+                        "Adding a profile is restricted: User %s Has device owner? %b",
+                        callingUserHandle, hasDeviceOwner));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
             // If there's a restriction on removing the managed profile then we have to take it
@@ -13069,6 +13097,8 @@
                     !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
                     callingUserHandle);
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
+                Slog.i(LOG_TAG, String.format(
+                        "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
@@ -15650,7 +15680,7 @@
     }
 
     @Override
-    public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
+    public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
@@ -15669,7 +15699,7 @@
         }
     }
 
-    private @PersonalAppSuspensionReason int makeSuspensionReasons(
+    private @PersonalAppsSuspensionReason int makeSuspensionReasons(
             boolean explicit, boolean timeout) {
         int result = PERSONAL_APPS_NOT_SUSPENDED;
         if (explicit) {
@@ -15793,7 +15823,7 @@
     }
 
     private void applyPersonalAppsSuspension(
-            int profileUserId, @PersonalAppSuspensionReason int suspensionState) {
+            int profileUserId, @PersonalAppsSuspensionReason int suspensionState) {
         final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended;
         final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED;
         if (suspended != shouldSuspend) {
@@ -15813,8 +15843,9 @@
         mInjector.binderWithCleanCallingIdentity(() -> {
             try {
                 final String[] appsToSuspend =
-                        new PersonalAppsSuspensionHelper(mContext, mInjector.getPackageManager())
-                                .getPersonalAppsForSuspension(userId);
+                        new PersonalAppsSuspensionHelper(
+                                mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
+                                .getPersonalAppsForSuspension();
                 final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
                         appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
                 if (!ArrayUtils.isEmpty(failedPackages)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 180acc8..d9db17e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -20,7 +20,6 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,7 +31,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.Log;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
@@ -43,7 +42,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -56,18 +54,21 @@
     private final Context mContext;
     private final PackageManager mPackageManager;
 
-    public PersonalAppsSuspensionHelper(Context context, PackageManager packageManager) {
+    /**
+     * @param context Context for the user whose apps should to be suspended.
+     */
+    public PersonalAppsSuspensionHelper(Context context) {
         mContext = context;
-        mPackageManager = packageManager;
+        mPackageManager = context.getPackageManager();
     }
 
     /**
      * @return List of packages that should be suspended to limit personal use.
      */
-    String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
+    String[] getPersonalAppsForSuspension() {
         final List<PackageInfo> installedPackageInfos =
-                mPackageManager.getInstalledPackagesAsUser(0 /* flags */, userId);
-        final Set<String> result = new HashSet<>();
+                mPackageManager.getInstalledPackages(0 /* flags */);
+        final Set<String> result = new ArraySet<>();
         for (final PackageInfo packageInfo : installedPackageInfos) {
             final ApplicationInfo info = packageInfo.applicationInfo;
             if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
@@ -77,11 +78,15 @@
         }
         result.removeAll(getCriticalPackages());
         result.removeAll(getSystemLauncherPackages());
-        result.removeAll(getAccessibilityServices(userId));
-        result.removeAll(getInputMethodPackages(userId));
-        result.remove(getActiveLauncherPackages(userId));
-        result.remove(getDialerPackage(userId));
-        result.remove(getSettingsPackageName(userId));
+        result.removeAll(getAccessibilityServices());
+        result.removeAll(getInputMethodPackages());
+        result.remove(getSettingsPackageName());
+
+        final String[] unsuspendablePackages =
+                mPackageManager.getUnsuspendablePackages(result.toArray(new String[0]));
+        for (final String pkg : unsuspendablePackages) {
+            result.remove(pkg);
+        }
 
         Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
         return result.toArray(new String[0]);
@@ -104,7 +109,6 @@
                 final ApplicationInfo applicationInfo =
                         mPackageManager.getApplicationInfo(packageName, 0);
                 if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
-                    Log.d(LOG_TAG, "Not suspending system launcher package: " + packageName);
                     result.add(packageName);
                 }
             } catch (PackageManager.NameNotFoundException e) {
@@ -114,81 +118,53 @@
         return result;
     }
 
-    private List<String> getAccessibilityServices(int userId) {
+    private List<String> getAccessibilityServices() {
         final List<AccessibilityServiceInfo> accessibilityServiceInfos =
-                getAccessibilityManagerForUser(userId)
+                getAccessibilityManagerForUser(mContext.getUserId())
                         .getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
         final List<String> result = new ArrayList<>();
         for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
             final ComponentName componentName =
                     ComponentName.unflattenFromString(serviceInfo.getId());
             if (componentName != null) {
-                final String packageName = componentName.getPackageName();
-                Slog.d(LOG_TAG, "Not suspending a11y service: " + packageName);
-                result.add(packageName);
+                result.add(componentName.getPackageName());
             }
         }
         return result;
     }
 
-    private List<String> getInputMethodPackages(int userId) {
-        final List<InputMethodInfo> enabledImes =
-                InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId);
+    private List<String> getInputMethodPackages() {
+        final List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
+                .getEnabledInputMethodListAsUser(mContext.getUserId());
         final List<String> result = new ArrayList<>();
         for (final InputMethodInfo info : enabledImes) {
-            Slog.d(LOG_TAG, "Not suspending IME: " + info.getPackageName());
             result.add(info.getPackageName());
         }
         return result;
     }
 
     @Nullable
-    private String getActiveLauncherPackages(int userId) {
-        final Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_HOME);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        return getPackageNameForIntent("active launcher", intent, userId);
-    }
-
-    @Nullable
-    private String getSettingsPackageName(int userId) {
+    private String getSettingsPackageName() {
         final Intent intent = new Intent(Settings.ACTION_SETTINGS);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
-        return getPackageNameForIntent("settings", intent, userId);
-    }
-
-    @Nullable
-    private String getDialerPackage(int userId) {
-        final Intent intent = new Intent(Intent.ACTION_DIAL);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        return getPackageNameForIntent("dialer", intent, userId);
-    }
-
-    @Nullable
-    private String getPackageNameForIntent(String name, Intent intent, int userId) {
-        final ResolveInfo resolveInfo =
-                mPackageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId);
+        final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0);
         if (resolveInfo != null) {
-            final String packageName = resolveInfo.activityInfo.packageName;
-            Slog.d(LOG_TAG, "Not suspending " + name + " package: " + packageName);
-            return packageName;
+            return resolveInfo.activityInfo.packageName;
         }
         return null;
     }
 
     private List<String> getCriticalPackages() {
-        final List<String> result = Arrays.asList(mContext.getResources()
+        return Arrays.asList(mContext.getResources()
                 .getStringArray(R.array.config_packagesExemptFromSuspension));
-        Slog.d(LOG_TAG, "Not suspending critical packages: " + String.join(",", result));
-        return result;
     }
 
     private boolean hasLauncherIntent(String packageName) {
         final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
         intentToResolve.setPackage(packageName);
-        final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(
-                intentToResolve, PackageManager.GET_UNINSTALLED_PACKAGES);
+        final List<ResolveInfo> resolveInfos =
+                mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0);
         return resolveInfos != null && !resolveInfos.isEmpty();
     }
 
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index fa0febd..acdb6814 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -419,6 +419,38 @@
                 .isTrue();
     }
 
+    @Test
+    public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() {
+        mockUninstallCrossProfileAppFromWorkProfile();
+        assertThat(mCrossProfileAppsServiceImpl
+                .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+                .isTrue();
+    }
+
+    @Test
+    public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()
+            throws Exception {
+        mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
+        assertThat(mCrossProfileAppsServiceImpl
+                .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+                .isFalse();
+    }
+
+    @Test
+    public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() {
+        mockCrossProfileAppNotWhitelisted();
+        assertThat(mCrossProfileAppsServiceImpl
+                .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+                .isTrue();
+    }
+
+    @Test
+    public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() {
+        assertThat(mCrossProfileAppsServiceImpl
+                .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+                .isTrue();
+    }
+
     private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
         explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index bb149cf..09af442 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -496,7 +496,7 @@
         // This one should get deferred on set
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
                 getNewMockPendingIntent());
-        final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+        final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
         assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
@@ -516,7 +516,7 @@
             mNowElapsedTest = mTestTimer.getElapsed();
             mTestTimer.expire();
         }
-        final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+        final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
         assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
@@ -676,7 +676,7 @@
         final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE);
         // The last alarm should now be deferred.
         final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota)
-                + mAppStandbyWindow + 1;
+                + mAppStandbyWindow;
         assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
@@ -695,7 +695,7 @@
             }
         }
         // The last alarm should be deferred due to exceeding the quota
-        final long deferredTrigger = firstTrigger + 1 + mAppStandbyWindow;
+        final long deferredTrigger = firstTrigger + mAppStandbyWindow;
         assertEquals(deferredTrigger, mTestTimer.getElapsed());
 
         // Upgrading the bucket now
@@ -730,7 +730,7 @@
             mTestTimer.expire();
         }
         // Any subsequent alarms in queue should all be deferred
-        assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed());
+        assertEquals(firstTrigger + mAppStandbyWindow, mTestTimer.getElapsed());
         // Paroling now
         assertAndHandleParoleChanged(true);
 
@@ -744,7 +744,7 @@
         assertAndHandleParoleChanged(false);
 
         // Subsequent alarms should again get deferred
-        final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow;
+        final long expectedNextTrigger = (firstTrigger + 5) + mAppStandbyWindow;
         assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1212f20..109c119 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -15,218 +15,227 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.servicestests">
+     package="com.android.frameworks.servicestests">
 
-    <uses-permission android:name="android.permission.READ_LOGS" />
-    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
-    <uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
-    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
-    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
-    <uses-permission android:name="android.permission.REORDER_TASKS" />
-    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.MANAGE_USERS" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
-    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
-    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
-    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
-    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
-    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
-    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
-    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
-    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER"/>
+    <uses-permission android:name="android.permission.ACCOUNT_MANAGER"/>
+    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+    <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
+    <uses-permission android:name="android.permission.GET_DETAILED_TASKS"/>
+    <uses-permission android:name="android.permission.REORDER_TASKS"/>
+    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
+    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.MANAGE_USERS"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"/>
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
+    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+    <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS"/>
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"/>
-    <uses-permission android:name="android.permission.DEVICE_POWER" />
-    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
-    <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-permission android:name="android.permission.READ_FRAME_BUFFER" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
-    <uses-permission android:name="android.permission.WATCH_APPOPS" />
+    <uses-permission android:name="android.permission.DEVICE_POWER"/>
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <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-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.STORAGE_INTERNAL"/>
+    <uses-permission android:name="android.permission.WATCH_APPOPS"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
-    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
-    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
-    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
-    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
-    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
     <uses-permission android:name="android.permission.BLUETOOTH"/>
-    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
-    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.DUMP"/>
     <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
-          android:targetSdkVersion="26"/>
+         android:targetSdkVersion="26"/>
 
     <application android:testOnly="true">
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
 
         <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
-            android:exported="false">
+             android:exported="false">
           <intent-filter>
-            <action android:name="android.accounts.AccountAuthenticator" />
+            <action android:name="android.accounts.AccountAuthenticator"/>
           </intent-filter>
           <meta-data android:name="android.accounts.AccountAuthenticator"
-              android:resource="@xml/test_account_type1_authenticator" />
+               android:resource="@xml/test_account_type1_authenticator"/>
         </service>
 
         <service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService"
-            android:exported="false">
+             android:exported="false">
           <intent-filter>
-            <action android:name="android.accounts.AccountAuthenticator" />
+            <action android:name="android.accounts.AccountAuthenticator"/>
           </intent-filter>
           <meta-data android:name="android.accounts.AccountAuthenticator"
-              android:resource="@xml/test_account_type2_authenticator" />
+               android:resource="@xml/test_account_type2_authenticator"/>
         </service>
 
         <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
-                android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                       android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <service android:name="com.android.server.job.MockPriorityJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
+             android:permission="android.permission.BIND_JOB_SERVICE"/>
 
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/>
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/>
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/>
 
         <activity android:name="com.android.server.pm.ShortcutTestActivity"
-                 android:enabled="true" android:exported="true" />
+             android:enabled="true"
+             android:exported="true"/>
 
         <activity android:name="com.android.server.pm.SuspendedDetailsActivity"
-                  android:enabled="true"
-                  android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
+             android:enabled="true"
+             android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
             <intent-filter>
-                <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
-                <category android:name="android.intent.category.DEFAULT" />
+                <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS"/>
+                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
 
-        <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
-        <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" />
+        <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity"/>
+        <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity"/>
 
         <activity-alias android:name="a.ShortcutEnabled"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="true">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="true">
         </activity-alias>
         <activity-alias android:name="a.ShortcutDisabled"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="false" android:exported="true">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="false"
+             android:exported="true">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_5"/>
         </activity-alias>
         <activity-alias android:name="a.ShortcutUnexported"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="false">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="false">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_5"/>
         </activity-alias>
         <activity-alias android:name="a.Shortcut1"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="true">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="true">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_1"/>
         </activity-alias>
         <activity-alias android:name="a.ShortcutConfigActivity"
-                        android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity">
             <intent-filter>
-                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <action android:name="android.intent.action.CREATE_SHORTCUT"/>
             </intent-filter>
         </activity-alias>
 
         <activity-alias android:name="a.DisabledMain"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="false" android:exported="true">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="false"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
 
         <activity-alias android:name="a.UnexportedMain"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="false">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="false">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
 
         <receiver android:name="com.android.server.appwidget.DummyAppWidget">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-              android:resource="@xml/dummy_appwidget_info" />
+                 android:resource="@xml/dummy_appwidget_info"/>
         </receiver>
     </application>
 
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.frameworks.servicestests"
-        android:label="Frameworks Services Tests" />
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.frameworks.servicestests"
+         android:label="Frameworks Services Tests"/>
 </manifest>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7c6ac17..baf551e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3205,6 +3205,7 @@
         when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
                 .thenReturn(true);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+        when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
     }
@@ -3246,6 +3247,7 @@
         when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
                 .thenReturn(true);
         setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+        when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
     }
@@ -3617,14 +3619,14 @@
 
         when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
-        when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+        when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
         when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
             .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
         when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
                 true)).thenReturn(true);
         setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
 
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
     }
 
     public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index bb51471..d244e68 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -61,8 +61,8 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
-import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.test.mock.MockContentResolver;
@@ -84,6 +84,7 @@
 import com.android.server.power.batterysaver.BatterySaverPolicy;
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
 import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.testutils.OffsettableClock;
 
 import org.junit.After;
 import org.junit.Before;
@@ -132,6 +133,8 @@
     private BatteryReceiver mBatteryReceiver;
     private UserSwitchedReceiver mUserSwitchedReceiver;
     private Resources mResourcesSpy;
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
 
     private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
         private final IntentFilter mFilter;
@@ -189,6 +192,9 @@
 
         Settings.Global.putInt(mContextSpy.getContentResolver(),
                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
     }
 
     private PowerManagerService createService() {
@@ -250,6 +256,16 @@
             }
 
             @Override
+            PowerManagerService.Clock createClock() {
+                return () -> mClock.now();
+            }
+
+            @Override
+            Handler createHandler(Looper looper, Handler.Callback callback) {
+                return new Handler(mTestLooper.getLooper(), callback);
+            }
+
+            @Override
             void invalidateIsInteractiveCaches() {
                 // Avoids an SELinux failure.
             }
@@ -297,21 +313,21 @@
     }
 
     private void forceSleep() {
-        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
     }
 
     private void forceDream() {
-        mService.getBinderServiceInstance().nap(SystemClock.uptimeMillis());
+        mService.getBinderServiceInstance().nap(mClock.now());
     }
 
     private void forceAwake() {
-        mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
     }
 
     private void forceDozing() {
-        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
     }
 
@@ -341,6 +357,11 @@
                 .thenReturn(minimumScreenOffTimeoutConfigMillis);
     }
 
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
     @Test
     public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() {
         createService();
@@ -403,7 +424,7 @@
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
 
         // Take a nap and verify.
-        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
@@ -445,7 +466,7 @@
         createService();
         startSystem();
         forceSleep();
-        mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
@@ -540,7 +561,7 @@
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
 
         // Take a nap and verify.
-        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
     }
@@ -550,7 +571,7 @@
         int interval = 1000;
         createService();
         mService.onUserActivity();
-        SystemClock.sleep(interval + 1 /* just a little more */);
+        advanceTime(interval + 1 /* just a little more */);
         assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
     }
 
@@ -678,7 +699,7 @@
         mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
                 null /* workSource */, null /* historyTag */);
         when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
-        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
         assertFalse(isAcquired[0]);
@@ -718,16 +739,16 @@
         createService();
         startSystem();
 
-        mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().userActivity(mClock.now(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
         verify(mInattentiveSleepWarningControllerMock, never()).show();
 
-        SystemClock.sleep(150);
+        advanceTime(150);
         verify(mInattentiveSleepWarningControllerMock, times(1)).show();
         verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean());
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
 
-        mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+        mService.getBinderServiceInstance().userActivity(mClock.now(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
         verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true);
     }
@@ -740,10 +761,10 @@
 
         createService();
         startSystem();
-        SystemClock.sleep(50);
+        advanceTime(50);
         verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show();
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
-        SystemClock.sleep(70);
+        advanceTime(70);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
         forceAwake();
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -764,7 +785,7 @@
         setAttentiveTimeout(5);
         createService();
         startSystem();
-        SystemClock.sleep(20);
+        advanceTime(20);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
 
@@ -772,7 +793,7 @@
     public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
         final String pkg = mContextSpy.getOpPackageName();
         final Binder token = new Binder();
-        final String tag = "sleep_testWithWakeLock";
+        final String tag = "testInattentiveSleep_goesToSleepWithWakeLock";
 
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveTimeout(30);
@@ -783,7 +804,7 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
                 null /* workSource */, null /* historyTag */);
 
-        SystemClock.sleep(60);
+        advanceTime(60);
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
index d7fc97c..2578ca8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -33,6 +34,7 @@
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -110,8 +112,10 @@
 
     void setUpShortcutBubble(boolean isValid) {
         when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
-        when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
-                .thenReturn(isValid);
+        ShortcutInfo info = mock(ShortcutInfo.class);
+        when(info.getId()).thenReturn(SHORTCUT_ID);
+        when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+                .thenReturn(isValid ? info : null);
         when(mBubbleMetadata.getIntent()).thenReturn(null);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9db7d5e..d2417f9 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
-import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -5237,141 +5236,6 @@
     }
 
     @Test
-    public void testFlagBubbleNotifs_flag_phonecall() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it a phone call
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setCategory(CATEGORY_CALL)
-                .addPerson(person)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_flag_phonecall", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        // Make sure it has foreground service
-        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        // yes phone call, yes person, yes foreground service, yes bubble
-        assertTrue(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_phonecall_noForegroundService() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it a phone call
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setCategory(CATEGORY_CALL)
-                .addPerson(person)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        // yes phone call, yes person, NO foreground service, no bubble
-        assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_phonecall_noPerson() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Make it a phone call
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setCategory(CATEGORY_CALL)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_phonecall_noPerson", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        // Make sure it has foreground service
-        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        // yes phone call, yes foreground service, BUT NO person, no bubble
-        assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_phonecall_noCategory() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // No category
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .addPerson(person)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_phonecall_noCategory", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        // Make sure it has foreground service
-        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        // yes person, yes foreground service, BUT NO call, no bubble
-        assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
     public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException {
         // Bubbles are NOT allowed!
         setUpPrefsForBubbles(PKG, mUid, true /* global */, false /* app */, true /* channel */);
@@ -5432,77 +5296,6 @@
     }
 
     @Test
-    public void testFlagBubbleNotifs_noFlag_phonecall_notAllowed() throws RemoteException {
-        // Bubbles are not allowed!
-        setUpPrefsForBubbles(PKG, mUid, false /* global */, true /* app */, true /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it a phone call
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setCategory(CATEGORY_CALL)
-                .addPerson(person)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_phonecall_notAllowed", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        // Make sure it has foreground service
-        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                sbn.getId(), sbn.getNotification(), sbn.getUserId());
-        waitForIdle();
-
-        // yes phone call, yes person, yes foreground service, but not allowed, no bubble
-        assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed() throws RemoteException {
-        // Bubbles are allowed, but not on channel.
-        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, false /* channel */);
-
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it a phone call
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setCategory(CATEGORY_CALL)
-                .addPerson(person)
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        // Make sure it has foreground service
-        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        // yes phone call, yes person, yes foreground service, but channel not allowed, no bubble
-        assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
     public void testCancelNotificationsFromApp_cancelsBubbles() throws Exception {
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
         nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
@@ -6579,13 +6372,41 @@
 
         ShortcutInfo si = mock(ShortcutInfo.class);
         when(si.getShortLabel()).thenReturn("Hello");
+        when(si.isLongLived()).thenReturn(true);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
 
         List<ConversationChannelWrapper> conversations =
                 mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
         assertEquals(si, conversations.get(0).getShortcutInfo());
         assertEquals(si, conversations.get(1).getShortcutInfo());
+    }
 
+    @Test
+    public void testGetConversationsForPackage_shortcut_notLongLived() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.getShortLabel()).thenReturn("Hello");
+        when(si.isLongLived()).thenReturn(false);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertNull(conversations.get(0).getShortcutInfo());
+        assertNull(conversations.get(1).getShortcutInfo());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 658ba1b..6ec0f91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1020,10 +1020,10 @@
             mSecondary = TaskTile.forToken(secondary.asBinder());
         }
         @Override
-        public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+        public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
         }
         @Override
-        public void taskVanished(ActivityManager.RunningTaskInfo info) {
+        public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
         }
         @Override
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index cc9173a..12bdec6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -78,11 +78,11 @@
         final Feature bar;
 
         DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
-                .addFeature(foo = new Feature.Builder(mPolicy, "Foo")
+                .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
                         .upTo(TYPE_STATUS_BAR)
                         .and(TYPE_NAVIGATION_BAR)
                         .build())
-                .addFeature(bar = new Feature.Builder(mPolicy, "Bar")
+                .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
                         .all()
                         .except(TYPE_STATUS_BAR)
                         .build())
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 5359bd3..a96f401 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -60,7 +60,6 @@
 import android.util.ArrayMap;
 import android.util.Rational;
 import android.view.Display;
-import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.WindowContainerTransaction;
 
@@ -103,10 +102,10 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         task.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any());
+        verify(organizer).onTaskAppeared(any());
 
         task.removeImmediately();
-        verify(organizer).taskVanished(any());
+        verify(organizer).onTaskVanished(any());
     }
 
     @Test
@@ -117,10 +116,10 @@
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
         task.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any());
+        verify(organizer).onTaskAppeared(any());
         task.setTaskOrganizer(organizer2);
-        verify(organizer).taskVanished(any());
-        verify(organizer2).taskAppeared(any());
+        verify(organizer).onTaskVanished(any());
+        verify(organizer2).onTaskAppeared(any());
     }
 
     @Test
@@ -131,10 +130,10 @@
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer).taskAppeared(any());
+        verify(organizer).onTaskAppeared(any());
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
-        verify(organizer).taskVanished(any());
-        verify(organizer2).taskAppeared(any());
+        verify(organizer).onTaskVanished(any());
+        verify(organizer2).onTaskAppeared(any());
     }
 
     @Test
@@ -144,12 +143,12 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any());
-        assertTrue(stack.isControlledByTaskOrganizer());
+        verify(organizer).onTaskAppeared(any());
+        assertTrue(stack.isOrganized());
 
         stack.setTaskOrganizer(null);
-        verify(organizer).taskVanished(any());
-        assertFalse(stack.isControlledByTaskOrganizer());
+        verify(organizer).onTaskVanished(any());
+        assertFalse(stack.isOrganized());
     }
 
     @Test
@@ -159,12 +158,12 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer).taskAppeared(any());
-        assertTrue(stack.isControlledByTaskOrganizer());
+        verify(organizer).onTaskAppeared(any());
+        assertTrue(stack.isOrganized());
 
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
-        verify(organizer).taskVanished(any());
-        assertFalse(stack.isControlledByTaskOrganizer());
+        verify(organizer).onTaskVanished(any());
+        assertFalse(stack.isOrganized());
     }
 
     @Test
@@ -179,23 +178,23 @@
 
         // First organizer is registered, verify a task appears when changing windowing mode
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer, times(1)).taskAppeared(any());
-        assertTrue(stack.isControlledByTaskOrganizer());
+        verify(organizer, times(1)).onTaskAppeared(any());
+        assertTrue(stack.isOrganized());
 
         // Now we replace the registration and1 verify the new organizer receives tasks
         // newly entering the windowing mode.
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
         stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer2).taskAppeared(any());
-        assertTrue(stack2.isControlledByTaskOrganizer());
+        verify(organizer2).onTaskAppeared(any());
+        assertTrue(stack2.isOrganized());
 
         // Now we unregister the second one, the first one should automatically be reregistered
         // so we verify that it's now seeing changes.
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
 
         stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer, times(2)).taskAppeared(any());
-        assertTrue(stack3.isControlledByTaskOrganizer());
+        verify(organizer, times(2)).onTaskAppeared(any());
+        assertTrue(stack3.isOrganized());
     }
 
     @Test
@@ -206,10 +205,10 @@
         final Task task = createTaskInStack(stack, 0 /* userId */);
         final Task task2 = createTaskInStack(stack, 0 /* userId */);
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
-        verify(organizer, times(1)).taskAppeared(any());
+        verify(organizer, times(1)).onTaskAppeared(any());
 
         stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        verify(organizer, times(1)).taskVanished(any());
+        verify(organizer, times(1)).onTaskVanished(any());
     }
 
     @Test
@@ -219,7 +218,7 @@
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
 
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
-        verify(organizer, times(1)).taskAppeared(any());
+        verify(organizer, times(1)).onTaskAppeared(any());
     }
 
     @Test
@@ -396,10 +395,10 @@
         final boolean[] called = {false};
         ITaskOrganizer listener = new ITaskOrganizer.Stub() {
             @Override
-            public void taskAppeared(RunningTaskInfo taskInfo) { }
+            public void onTaskAppeared(RunningTaskInfo taskInfo) { }
 
             @Override
-            public void taskVanished(RunningTaskInfo container) { }
+            public void onTaskVanished(RunningTaskInfo container) { }
 
             @Override
             public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
@@ -447,10 +446,10 @@
         final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
         ITaskOrganizer listener = new ITaskOrganizer.Stub() {
             @Override
-            public void taskAppeared(RunningTaskInfo taskInfo) { }
+            public void onTaskAppeared(RunningTaskInfo taskInfo) { }
 
             @Override
-            public void taskVanished(RunningTaskInfo container) { }
+            public void onTaskVanished(RunningTaskInfo container) { }
 
             @Override
             public void onTaskInfoChanged(RunningTaskInfo info) {
@@ -667,11 +666,11 @@
         RunningTaskInfo mInfo;
 
         @Override
-        public void taskAppeared(RunningTaskInfo info) {
+        public void onTaskAppeared(RunningTaskInfo info) {
             mInfo = info;
         }
         @Override
-        public void taskVanished(RunningTaskInfo info) {
+        public void onTaskVanished(RunningTaskInfo info) {
         }
         @Override
         public void onTaskInfoChanged(RunningTaskInfo info) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a25acae..5848295 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -27,6 +27,7 @@
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
@@ -939,6 +940,20 @@
         verify(persister, never()).saveTask(same(task), any());
     }
 
+    @Test
+    public void testNotSpecifyOrientationByFloatingTask() {
+        final Task task = getTestTask();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final WindowContainer<?> taskContainer = task.getParent();
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskContainer.getOrientation());
+
+        task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+        assertEquals(SCREEN_ORIENTATION_UNSET, taskContainer.getOrientation());
+    }
+
     private Task getTestTask() {
         final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
         return stack.getBottomMostTask();
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 3196758..00cb6dc 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -207,7 +207,13 @@
 
     @Override
     public void onBootPhase(int phase) {
-        if (PHASE_SYSTEM_SERVICES_READY == phase) {
+        Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
+        if (PHASE_DEVICE_SPECIFIC_SERVICES_READY == phase) {
+            if (isSafeMode()) {
+                Slog.w(TAG, "not enabling SoundTriggerService in safe mode");
+                return;
+            }
+
             initSoundTriggerHelper();
             mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
         } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 488ee78..47bf148 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -23,6 +23,9 @@
 
 import com.android.server.wm.ActivityMetricsLaunchObserver;
 
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
 /**
  * A validator to check the correctness of event sequence during app startup.
  *
@@ -100,7 +103,8 @@
   @Override
   public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
+      logWarningWithStackTrace(
+          String.format("IntentStarted during UNKNOWN. " + intent));
       incAccIntentStartedEvents();
       return;
     }
@@ -110,7 +114,7 @@
         state != State.ACTIVITY_CANCELLED &&
         state != State.ACTIVITY_FINISHED &&
         state != State.REPORT_FULLY_DRAWN) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
       incAccIntentStartedEvents();
       incAccIntentStartedEvents();
@@ -124,12 +128,12 @@
   @Override
   public void onIntentFailed() {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "IntentFailed during UNKNOWN.");
+      logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
       incAccIntentStartedEvents();
       return;
@@ -143,11 +147,12 @@
   public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
       @Temperature int temperature) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunched during UNKNOWN."));
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
       incAccIntentStartedEvents();
       return;
@@ -160,12 +165,13 @@
   @Override
   public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunchCancelled during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
       incAccIntentStartedEvents();
       return;
@@ -179,13 +185,14 @@
   public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunchFinished during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
 
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
       incAccIntentStartedEvents();
       return;
@@ -199,7 +206,8 @@
   public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onReportFullyDrawn during UNKNOWN."));
       return;
     }
     if (state == State.INIT) {
@@ -207,7 +215,7 @@
     }
 
     if (state != State.ACTIVITY_FINISHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
       return;
     }
@@ -252,4 +260,11 @@
     Log.i(TAG,
         String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
   }
+
+  private void logWarningWithStackTrace(String log) {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+    Log.w(TAG, String.format("%s\n%s", log, sw));
+  }
 }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a6c5c3b..3546434 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -449,13 +449,21 @@
     }
 
     /**
-     * @return the number of this subscription.
+     * @return the number of this subscription if the calling app has been granted the
+     * READ_PHONE_NUMBERS permission, or an empty string otherwise
      */
     public String getNumber() {
         return mNumber;
     }
 
     /**
+     * @hide
+     */
+    public void clearNumber() {
+        mNumber = "";
+    }
+
+    /**
      * @return the data roaming state for this subscription, either
      * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
      */
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2f9a1c8..9be97b5 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -509,16 +509,68 @@
         for (int i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
             IorapCompilationStatus status = waitForIorapCompiled(appPkgName);
             if (status == IorapCompilationStatus.COMPLETE) {
+                Log.v(TAG, "compileAppForIorap: success");
+                logDumpsysIorapd(appPkgName);
                 return true;
             } else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
+                Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
+                logDumpsysIorapd(appPkgName);
                 return false;
             } // else INCOMPLETE. keep asking iorapd if it's done yet.
             sleep(1000);
         }
 
+        Log.e(TAG, "compileAppForIorap: failed due to timeout");
+        logDumpsysIorapd(appPkgName);
         return false;
     }
 
+    /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
+    private void logDumpsysIorapd(String packageName) throws IOException {
+        InstrumentationTestRunner instrumentation =
+                (InstrumentationTestRunner)getInstrumentation();
+        Bundle args = instrumentation.getArguments();
+
+        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+        // Root directory for applaunch file to log the app launch output
+        // Will be useful in case of simpleperf command is used
+        File launchRootDir = null;
+        if (null != launchDirectory && !launchDirectory.isEmpty()) {
+            launchRootDir = new File(launchDirectory);
+            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+                throw new IOException("Unable to create the destination directory "
+                    + launchRootDir + ". Try disabling selinux.");
+            }
+        } else {
+            Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg");
+            return;
+        }
+
+        File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+        if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+            throw new IOException("Unable to create the lauch file sub directory "
+                + launchSubDir + ". Try disabling selinux.");
+        }
+        String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt";
+        File file = new File(launchSubDir, path);
+        try (FileOutputStream outputStream = new FileOutputStream(file);
+                BufferedWriter writer = new BufferedWriter(
+                        new OutputStreamWriter(outputStream));
+                ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                        executeShellCommand(IORAP_DUMPSYS_CMD);
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                writer.write(line + "\n");
+            }
+        }
+
+        Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path);
+    }
+
     enum IorapCompilationStatus {
         INCOMPLETE,
         COMPLETE,
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index d7f8204..7e97fa3 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -23,6 +23,10 @@
         <option name="force-skip-system-props" value="true" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+        <option name="cleanup-action" value="REBOOT" />
+    </target_preparer>
+
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.bootimageprofile.BootImageProfileTest" />
     </test>
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index f186ed3..5afd39e 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -150,7 +150,7 @@
         };
 
         @Override
-        public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+        public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) {
             if (!gotFirstTask) {
                 mTaskView1.reparentTask(ti.token);
                 gotFirstTask = true;
@@ -158,7 +158,7 @@
                 mTaskView2.reparentTask(ti.token);
             }
         }
-        public void taskVanished(ActivityManager.RunningTaskInfo ti) {
+        public void onTaskVanished(ActivityManager.RunningTaskInfo ti) {
         }
         @Override
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index a2f40dc..520bc25 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -38,7 +38,7 @@
     TaskView mTaskView;
 
     class Organizer extends ITaskOrganizer.Stub {
-        public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+        public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) {
             mTaskView.reparentTask(ti.token);
 
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -48,7 +48,7 @@
             } catch (Exception e) {
             }
         }
-        public void taskVanished(ActivityManager.RunningTaskInfo ti) {
+        public void onTaskVanished(ActivityManager.RunningTaskInfo ti) {
         }
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         }
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 548af0c..498cb7c 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 
@@ -31,6 +30,7 @@
 import android.graphics.Insets;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -44,11 +44,11 @@
 import android.view.animation.LinearInterpolator;
 import android.widget.LinearLayout;
 
-import androidx.appcompat.app.AppCompatActivity;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.appcompat.app.AppCompatActivity;
+
 public class WindowInsetsActivity extends AppCompatActivity {
 
     private View mRoot;
@@ -191,6 +191,40 @@
                 mTransitions.forEach(it -> it.onFinish(animation));
             }
         });
+
+        findViewById(R.id.floating_action_button).setOnClickListener(
+                v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1,
+                new LinearInterpolator(), null /* cancellationSignal */,
+                new WindowInsetsAnimationControlListener() {
+                    @Override
+                    public void onReady(
+                            WindowInsetsAnimationController controller,
+                            int types) {
+                        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+                        anim.setDuration(1500);
+                        anim.addUpdateListener(animation
+                                -> controller.setInsetsAndAlpha(
+                                controller.getShownStateInsets(),
+                                (float) animation.getAnimatedValue(),
+                                anim.getAnimatedFraction()));
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                super.onAnimationEnd(animation);
+                                controller.finish(true);
+                            }
+                        });
+                        anim.start();
+                    }
+
+                    @Override
+                    public void onCancelled(WindowInsetsAnimationController controller) {
+                    }
+
+                    @Override
+                    public void onFinished(WindowInsetsAnimationController controller) {
+                    }
+                }));
     }
 
     @Override
@@ -200,57 +234,6 @@
         getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
     }
 
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getWindow().getInsetsController().addOnControllableInsetsChangedListener(
-                new OnControllableInsetsChangedListener() {
-
-                    boolean hasControl = false;
-                    @Override
-                    public void onControllableInsetsChanged(WindowInsetsController controller,
-                            int types) {
-                        if ((types & ime()) != 0 && !hasControl) {
-                            hasControl = true;
-                            controller.controlWindowInsetsAnimation(ime(), -1,
-                                    new LinearInterpolator(), null /* cancellationSignal */,
-                                    new WindowInsetsAnimationControlListener() {
-                                        @Override
-                                        public void onReady(
-                                                WindowInsetsAnimationController controller,
-                                                int types) {
-                                            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-                                            anim.setDuration(1500);
-                                            anim.addUpdateListener(animation
-                                                    -> controller.setInsetsAndAlpha(
-                                                    controller.getShownStateInsets(),
-                                                    (float) animation.getAnimatedValue(),
-                                                    anim.getAnimatedFraction()));
-                                            anim.addListener(new AnimatorListenerAdapter() {
-                                                @Override
-                                                public void onAnimationEnd(Animator animation) {
-                                                    super.onAnimationEnd(animation);
-                                                    controller.finish(true);
-                                                }
-                                            });
-                                            anim.start();
-                                        }
-
-                                        @Override
-                                        public void onFinished(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-
-                                        @Override
-                                        public void onCancelled(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-                                    });
-                        }
-                    }
-                });
-    }
-
     static class Transition {
         private int mEndBottom;
         private int mStartBottom;
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index b2e8c37..916c339 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -463,7 +463,9 @@
 
         nc1.setSSID(TEST_SSID);
         nc2.combineCapabilities(nc1);
-        assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         // Because they now have the same SSID, the following call should not throw
         nc2.combineCapabilities(nc1);
@@ -601,12 +603,16 @@
         // from nc2.
         assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setSSID(DIFFERENT_TEST_SSID);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
-        assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 01bd47b..a826646 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -48,6 +48,8 @@
     private static final Method MESSAGE_MARK_IN_USE_METHOD;
     private static final String TAG = "TestLooper";
 
+    private final Clock mClock;
+
     private AutoDispatchThread mAutoDispatchThread;
 
     static {
@@ -69,8 +71,25 @@
         }
     }
 
-
+    /**
+     * Creates a TestLooper and installs it as the looper for the current thread.
+     */
     public TestLooper() {
+        this(SystemClock::uptimeMillis);
+    }
+
+    /**
+     * Creates a TestLooper with a custom clock and installs it as the looper for the current
+     * thread.
+     *
+     * Messages are dispatched when their {@link Message#when} is before or at {@link
+     * Clock#uptimeMillis()}.
+     * Use a custom clock with care. When using an offsettable clock like {@link
+     * com.android.server.testutils.OffsettableClock} be sure not to double offset messages by
+     * offsetting the clock and calling {@link #moveTimeForward(long)}. Instead, offset the clock
+     * and call {@link #dispatchAll()}.
+     */
+    public TestLooper(Clock clock) {
         try {
             mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
 
@@ -80,6 +99,8 @@
         } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
             throw new RuntimeException("Reflection error constructing or accessing looper", e);
         }
+
+        mClock = clock;
     }
 
     public Looper getLooper() {
@@ -116,9 +137,13 @@
         }
     }
 
+    private long currentTime() {
+        return mClock.uptimeMillis();
+    }
+
     private Message messageQueueNext() {
         try {
-            long now = SystemClock.uptimeMillis();
+            long now = currentTime();
 
             Message prevMsg = null;
             Message msg = getMessageLinkedList();
@@ -157,7 +182,7 @@
     public synchronized boolean isIdle() {
         Message messageList = getMessageLinkedList();
 
-        return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen();
+        return messageList != null && currentTime() >= messageList.getWhen();
     }
 
     /**
@@ -187,6 +212,7 @@
     /**
      * Dispatch all messages currently in the queue
      * Will not fail if there are no messages pending
+     *
      * @return the number of messages dispatched
      */
     public synchronized int dispatchAll() {
@@ -198,6 +224,10 @@
         return count;
     }
 
+    public interface Clock {
+        long uptimeMillis();
+    }
+
     /**
      * Thread used to dispatch messages when the main thread is blocked waiting for a response.
      */
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 34e2e3a..ea5043a 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -30,5 +30,9 @@
     <object type="module_controller"
             class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
         <option name="mainline-module-package-name" value="com.google.android.wifi" />
+        <!-- TODO(b/151836001): com.android.wifi doesn't guarantee it is a Mainline module since
+              it could still be OEM customized. Workaround so that this test will still run on
+              AOSP builds. -->
+        <option name="mainline-module-package-name" value="com.android.wifi" />
     </object>
 </configuration>